1# Copyright 2017 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"""RSA verifier and signer that use the ``cryptography`` library.
16
17This is a much faster implementation than the default (in
18``google.auth.crypt._python_rsa``), which depends on the pure-Python
19``rsa`` library.
20"""
21
22import cryptography.exceptions
23from cryptography.hazmat import backends
24from cryptography.hazmat.primitives import hashes
25from cryptography.hazmat.primitives import serialization
26from cryptography.hazmat.primitives.asymmetric import padding
27import cryptography.x509
28
29from google.auth import _helpers
30from google.auth.crypt import base
31
32_CERTIFICATE_MARKER = b"-----BEGIN CERTIFICATE-----"
33_BACKEND = backends.default_backend()
34_PADDING = padding.PKCS1v15()
35_SHA256 = hashes.SHA256()
36
37
38class RSAVerifier(base.Verifier):
39    """Verifies RSA cryptographic signatures using public keys.
40
41    Args:
42        public_key (
43                cryptography.hazmat.primitives.asymmetric.rsa.RSAPublicKey):
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        message = _helpers.to_bytes(message)
53        try:
54            self._pubkey.verify(signature, message, _PADDING, _SHA256)
55            return True
56        except (ValueError, cryptography.exceptions.InvalidSignature):
57            return False
58
59    @classmethod
60    def from_string(cls, public_key):
61        """Construct an Verifier instance from a public key or public
62        certificate string.
63
64        Args:
65            public_key (Union[str, bytes]): The public key in PEM format or the
66                x509 public key certificate.
67
68        Returns:
69            Verifier: The constructed verifier.
70
71        Raises:
72            ValueError: If the public key can't be parsed.
73        """
74        public_key_data = _helpers.to_bytes(public_key)
75
76        if _CERTIFICATE_MARKER in public_key_data:
77            cert = cryptography.x509.load_pem_x509_certificate(
78                public_key_data, _BACKEND
79            )
80            pubkey = cert.public_key()
81
82        else:
83            pubkey = serialization.load_pem_public_key(public_key_data, _BACKEND)
84
85        return cls(pubkey)
86
87
88class RSASigner(base.Signer, base.FromServiceAccountMixin):
89    """Signs messages with an RSA private key.
90
91    Args:
92        private_key (
93                cryptography.hazmat.primitives.asymmetric.rsa.RSAPrivateKey):
94            The private key to sign with.
95        key_id (str): Optional key ID used to identify this private key. This
96            can be useful to associate the private key with its associated
97            public key or certificate.
98    """
99
100    def __init__(self, private_key, key_id=None):
101        self._key = private_key
102        self._key_id = key_id
103
104    @property
105    @_helpers.copy_docstring(base.Signer)
106    def key_id(self):
107        return self._key_id
108
109    @_helpers.copy_docstring(base.Signer)
110    def sign(self, message):
111        message = _helpers.to_bytes(message)
112        return self._key.sign(message, _PADDING, _SHA256)
113
114    @classmethod
115    def from_string(cls, key, key_id=None):
116        """Construct a RSASigner from a private key in PEM format.
117
118        Args:
119            key (Union[bytes, str]): Private key in PEM format.
120            key_id (str): An optional key id used to identify the private key.
121
122        Returns:
123            google.auth.crypt._cryptography_rsa.RSASigner: The
124            constructed signer.
125
126        Raises:
127            ValueError: If ``key`` is not ``bytes`` or ``str`` (unicode).
128            UnicodeDecodeError: If ``key`` is ``bytes`` but cannot be decoded
129                into a UTF-8 ``str``.
130            ValueError: If ``cryptography`` "Could not deserialize key data."
131        """
132        key = _helpers.to_bytes(key)
133        private_key = serialization.load_pem_private_key(
134            key, password=None, backend=_BACKEND
135        )
136        return cls(private_key, key_id=key_id)
137