1# Copyright 2017 Google Inc. 
    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"""ECDSA (ES256) verifier and signer that use the ``cryptography`` library. 
    16""" 
    17 
    18from cryptography import utils  # type: ignore 
    19import cryptography.exceptions 
    20from cryptography.hazmat import backends 
    21from cryptography.hazmat.primitives import hashes 
    22from cryptography.hazmat.primitives import serialization 
    23from cryptography.hazmat.primitives.asymmetric import ec 
    24from cryptography.hazmat.primitives.asymmetric import padding 
    25from cryptography.hazmat.primitives.asymmetric.utils import decode_dss_signature 
    26from cryptography.hazmat.primitives.asymmetric.utils import encode_dss_signature 
    27import cryptography.x509 
    28 
    29from google.auth import _helpers 
    30from google.auth.crypt import base 
    31 
    32 
    33_CERTIFICATE_MARKER = b"-----BEGIN CERTIFICATE-----" 
    34_BACKEND = backends.default_backend() 
    35_PADDING = padding.PKCS1v15() 
    36 
    37 
    38class ES256Verifier(base.Verifier): 
    39    """Verifies ECDSA cryptographic signatures using public keys. 
    40 
    41    Args: 
    42        public_key ( 
    43                cryptography.hazmat.primitives.asymmetric.ec.ECDSAPublicKey): 
    44            The public key used to verify signatures. 
    45    """ 
    46 
    47    def __init__(self, public_key): 
    48        self._pubkey = public_key 
    49 
    50    @_helpers.copy_docstring(base.Verifier) 
    51    def verify(self, message, signature): 
    52        # First convert (r||s) raw signature to ASN1 encoded signature. 
    53        sig_bytes = _helpers.to_bytes(signature) 
    54        if len(sig_bytes) != 64: 
    55            return False 
    56        r = ( 
    57            int.from_bytes(sig_bytes[:32], byteorder="big") 
    58            if _helpers.is_python_3() 
    59            else utils.int_from_bytes(sig_bytes[:32], byteorder="big") 
    60        ) 
    61        s = ( 
    62            int.from_bytes(sig_bytes[32:], byteorder="big") 
    63            if _helpers.is_python_3() 
    64            else utils.int_from_bytes(sig_bytes[32:], byteorder="big") 
    65        ) 
    66        asn1_sig = encode_dss_signature(r, s) 
    67 
    68        message = _helpers.to_bytes(message) 
    69        try: 
    70            self._pubkey.verify(asn1_sig, message, ec.ECDSA(hashes.SHA256())) 
    71            return True 
    72        except (ValueError, cryptography.exceptions.InvalidSignature): 
    73            return False 
    74 
    75    @classmethod 
    76    def from_string(cls, public_key): 
    77        """Construct an Verifier instance from a public key or public 
    78        certificate string. 
    79 
    80        Args: 
    81            public_key (Union[str, bytes]): The public key in PEM format or the 
    82                x509 public key certificate. 
    83 
    84        Returns: 
    85            Verifier: The constructed verifier. 
    86 
    87        Raises: 
    88            ValueError: If the public key can't be parsed. 
    89        """ 
    90        public_key_data = _helpers.to_bytes(public_key) 
    91 
    92        if _CERTIFICATE_MARKER in public_key_data: 
    93            cert = cryptography.x509.load_pem_x509_certificate( 
    94                public_key_data, _BACKEND 
    95            ) 
    96            pubkey = cert.public_key() 
    97 
    98        else: 
    99            pubkey = serialization.load_pem_public_key(public_key_data, _BACKEND) 
    100 
    101        return cls(pubkey) 
    102 
    103 
    104class ES256Signer(base.Signer, base.FromServiceAccountMixin): 
    105    """Signs messages with an ECDSA private key. 
    106 
    107    Args: 
    108        private_key ( 
    109                cryptography.hazmat.primitives.asymmetric.ec.ECDSAPrivateKey): 
    110            The private key to sign with. 
    111        key_id (str): Optional key ID used to identify this private key. This 
    112            can be useful to associate the private key with its associated 
    113            public key or certificate. 
    114    """ 
    115 
    116    def __init__(self, private_key, key_id=None): 
    117        self._key = private_key 
    118        self._key_id = key_id 
    119 
    120    @property  # type: ignore 
    121    @_helpers.copy_docstring(base.Signer) 
    122    def key_id(self): 
    123        return self._key_id 
    124 
    125    @_helpers.copy_docstring(base.Signer) 
    126    def sign(self, message): 
    127        message = _helpers.to_bytes(message) 
    128        asn1_signature = self._key.sign(message, ec.ECDSA(hashes.SHA256())) 
    129 
    130        # Convert ASN1 encoded signature to (r||s) raw signature. 
    131        (r, s) = decode_dss_signature(asn1_signature) 
    132        return ( 
    133            (r.to_bytes(32, byteorder="big") + s.to_bytes(32, byteorder="big")) 
    134            if _helpers.is_python_3() 
    135            else (utils.int_to_bytes(r, 32) + utils.int_to_bytes(s, 32)) 
    136        ) 
    137 
    138    @classmethod 
    139    def from_string(cls, key, key_id=None): 
    140        """Construct a RSASigner from a private key in PEM format. 
    141 
    142        Args: 
    143            key (Union[bytes, str]): Private key in PEM format. 
    144            key_id (str): An optional key id used to identify the private key. 
    145 
    146        Returns: 
    147            google.auth.crypt._cryptography_rsa.RSASigner: The 
    148            constructed signer. 
    149 
    150        Raises: 
    151            ValueError: If ``key`` is not ``bytes`` or ``str`` (unicode). 
    152            UnicodeDecodeError: If ``key`` is ``bytes`` but cannot be decoded 
    153                into a UTF-8 ``str``. 
    154            ValueError: If ``cryptography`` "Could not deserialize key data." 
    155        """ 
    156        key = _helpers.to_bytes(key) 
    157        private_key = serialization.load_pem_private_key( 
    158            key, password=None, backend=_BACKEND 
    159        ) 
    160        return cls(private_key, key_id=key_id) 
    161 
    162    def __getstate__(self): 
    163        """Pickle helper that serializes the _key attribute.""" 
    164        state = self.__dict__.copy() 
    165        state["_key"] = self._key.private_bytes( 
    166            encoding=serialization.Encoding.PEM, 
    167            format=serialization.PrivateFormat.PKCS8, 
    168            encryption_algorithm=serialization.NoEncryption(), 
    169        ) 
    170        return state 
    171 
    172    def __setstate__(self, state): 
    173        """Pickle helper that deserializes the _key attribute.""" 
    174        state["_key"] = serialization.load_pem_private_key(state["_key"], None) 
    175        self.__dict__.update(state)