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
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
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