1# Copyright 2016 Google LLC 
    2# 
    3# Licensed under the Apache License, Version 2.0 (the "License"); 
    4# you may not use this file except in compliance with the License. 
    5# You may obtain a copy of the License at 
    6# 
    7#      http://www.apache.org/licenses/LICENSE-2.0 
    8# 
    9# Unless required by applicable law or agreed to in writing, software 
    10# distributed under the License is distributed on an "AS IS" BASIS, 
    11# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 
    12# See the License for the specific language governing permissions and 
    13# limitations under the License. 
    14 
    15"""Base classes for cryptographic signers and verifiers.""" 
    16 
    17import abc 
    18import io 
    19import json 
    20 
    21from google.auth import exceptions 
    22 
    23_JSON_FILE_PRIVATE_KEY = "private_key" 
    24_JSON_FILE_PRIVATE_KEY_ID = "private_key_id" 
    25 
    26 
    27class Verifier(metaclass=abc.ABCMeta): 
    28    """Abstract base class for crytographic signature verifiers.""" 
    29 
    30    @abc.abstractmethod 
    31    def verify(self, message, signature): 
    32        """Verifies a message against a cryptographic signature. 
    33 
    34        Args: 
    35            message (Union[str, bytes]): The message to verify. 
    36            signature (Union[str, bytes]): The cryptography signature to check. 
    37 
    38        Returns: 
    39            bool: True if message was signed by the private key associated 
    40            with the public key that this object was constructed with. 
    41        """ 
    42        # pylint: disable=missing-raises-doc,redundant-returns-doc 
    43        # (pylint doesn't recognize that this is abstract) 
    44        raise NotImplementedError("Verify must be implemented") 
    45 
    46 
    47class Signer(metaclass=abc.ABCMeta): 
    48    """Abstract base class for cryptographic signers.""" 
    49 
    50    @abc.abstractproperty 
    51    def key_id(self): 
    52        """Optional[str]: The key ID used to identify this private key.""" 
    53        raise NotImplementedError("Key id must be implemented") 
    54 
    55    @abc.abstractmethod 
    56    def sign(self, message): 
    57        """Signs a message. 
    58 
    59        Args: 
    60            message (Union[str, bytes]): The message to be signed. 
    61 
    62        Returns: 
    63            bytes: The signature of the message. 
    64        """ 
    65        # pylint: disable=missing-raises-doc,redundant-returns-doc 
    66        # (pylint doesn't recognize that this is abstract) 
    67        raise NotImplementedError("Sign must be implemented") 
    68 
    69 
    70class FromServiceAccountMixin(metaclass=abc.ABCMeta): 
    71    """Mix-in to enable factory constructors for a Signer.""" 
    72 
    73    @abc.abstractmethod 
    74    def from_string(cls, key, key_id=None): 
    75        """Construct an Signer instance from a private key string. 
    76 
    77        Args: 
    78            key (str): Private key as a string. 
    79            key_id (str): An optional key id used to identify the private key. 
    80 
    81        Returns: 
    82            google.auth.crypt.Signer: The constructed signer. 
    83 
    84        Raises: 
    85            ValueError: If the key cannot be parsed. 
    86        """ 
    87        raise NotImplementedError("from_string must be implemented") 
    88 
    89    @classmethod 
    90    def from_service_account_info(cls, info): 
    91        """Creates a Signer instance instance from a dictionary containing 
    92        service account info in Google format. 
    93 
    94        Args: 
    95            info (Mapping[str, str]): The service account info in Google 
    96                format. 
    97 
    98        Returns: 
    99            google.auth.crypt.Signer: The constructed signer. 
    100 
    101        Raises: 
    102            ValueError: If the info is not in the expected format. 
    103        """ 
    104        if _JSON_FILE_PRIVATE_KEY not in info: 
    105            raise exceptions.MalformedError( 
    106                "The private_key field was not found in the service account " "info." 
    107            ) 
    108 
    109        return cls.from_string( 
    110            info[_JSON_FILE_PRIVATE_KEY], info.get(_JSON_FILE_PRIVATE_KEY_ID) 
    111        ) 
    112 
    113    @classmethod 
    114    def from_service_account_file(cls, filename): 
    115        """Creates a Signer instance from a service account .json file 
    116        in Google format. 
    117 
    118        Args: 
    119            filename (str): The path to the service account .json file. 
    120 
    121        Returns: 
    122            google.auth.crypt.Signer: The constructed signer. 
    123        """ 
    124        with io.open(filename, "r", encoding="utf-8") as json_file: 
    125            data = json.load(json_file) 
    126 
    127        return cls.from_service_account_info(data)