blob: 5bfd57fb8838ae29f0316195a2d51d1a6670e228 [file] [log] [blame]
Thea Flowerse290a3d2020-04-01 10:11:42 -07001# 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
18import cryptography.exceptions
19from cryptography.hazmat import backends
20from cryptography.hazmat.primitives import hashes
21from cryptography.hazmat.primitives import serialization
22from cryptography.hazmat.primitives.asymmetric import ec
23from cryptography.hazmat.primitives.asymmetric import padding
24import cryptography.x509
25import pkg_resources
26
27from google.auth import _helpers
28from google.auth.crypt import base
29
30_IMPORT_ERROR_MSG = (
31 "cryptography>=1.4.0 is required to use cryptography-based ECDSA " "algorithms"
32)
33
34try: # pragma: NO COVER
35 release = pkg_resources.get_distribution("cryptography").parsed_version
36 if release < pkg_resources.parse_version("1.4.0"):
37 raise ImportError(_IMPORT_ERROR_MSG)
38except pkg_resources.DistributionNotFound: # pragma: NO COVER
39 raise ImportError(_IMPORT_ERROR_MSG)
40
41
42_CERTIFICATE_MARKER = b"-----BEGIN CERTIFICATE-----"
43_BACKEND = backends.default_backend()
44_PADDING = padding.PKCS1v15()
45
46
47class ES256Verifier(base.Verifier):
48 """Verifies ECDSA cryptographic signatures using public keys.
49
50 Args:
51 public_key (
52 cryptography.hazmat.primitives.asymmetric.ec.ECDSAPublicKey):
53 The public key used to verify signatures.
54 """
55
56 def __init__(self, public_key):
57 self._pubkey = public_key
58
59 @_helpers.copy_docstring(base.Verifier)
60 def verify(self, message, signature):
61 message = _helpers.to_bytes(message)
62 try:
63 self._pubkey.verify(signature, message, ec.ECDSA(hashes.SHA256()))
64 return True
65 except (ValueError, cryptography.exceptions.InvalidSignature):
66 return False
67
68 @classmethod
69 def from_string(cls, public_key):
70 """Construct an Verifier instance from a public key or public
71 certificate string.
72
73 Args:
74 public_key (Union[str, bytes]): The public key in PEM format or the
75 x509 public key certificate.
76
77 Returns:
78 Verifier: The constructed verifier.
79
80 Raises:
81 ValueError: If the public key can't be parsed.
82 """
83 public_key_data = _helpers.to_bytes(public_key)
84
85 if _CERTIFICATE_MARKER in public_key_data:
86 cert = cryptography.x509.load_pem_x509_certificate(
87 public_key_data, _BACKEND
88 )
89 pubkey = cert.public_key()
90
91 else:
92 pubkey = serialization.load_pem_public_key(public_key_data, _BACKEND)
93
94 return cls(pubkey)
95
96
97class ES256Signer(base.Signer, base.FromServiceAccountMixin):
98 """Signs messages with an ECDSA private key.
99
100 Args:
101 private_key (
102 cryptography.hazmat.primitives.asymmetric.ec.ECDSAPrivateKey):
103 The private key to sign with.
104 key_id (str): Optional key ID used to identify this private key. This
105 can be useful to associate the private key with its associated
106 public key or certificate.
107 """
108
109 def __init__(self, private_key, key_id=None):
110 self._key = private_key
111 self._key_id = key_id
112
113 @property
114 @_helpers.copy_docstring(base.Signer)
115 def key_id(self):
116 return self._key_id
117
118 @_helpers.copy_docstring(base.Signer)
119 def sign(self, message):
120 message = _helpers.to_bytes(message)
121 return self._key.sign(message, ec.ECDSA(hashes.SHA256()))
122
123 @classmethod
124 def from_string(cls, key, key_id=None):
125 """Construct a RSASigner from a private key in PEM format.
126
127 Args:
128 key (Union[bytes, str]): Private key in PEM format.
129 key_id (str): An optional key id used to identify the private key.
130
131 Returns:
132 google.auth.crypt._cryptography_rsa.RSASigner: The
133 constructed signer.
134
135 Raises:
136 ValueError: If ``key`` is not ``bytes`` or ``str`` (unicode).
137 UnicodeDecodeError: If ``key`` is ``bytes`` but cannot be decoded
138 into a UTF-8 ``str``.
139 ValueError: If ``cryptography`` "Could not deserialize key data."
140 """
141 key = _helpers.to_bytes(key)
142 private_key = serialization.load_pem_private_key(
143 key, password=None, backend=_BACKEND
144 )
145 return cls(private_key, key_id=key_id)