blob: c6d61760672848d88148ea9c50cac3715919c8d7 [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
arithmetic1728cf2c0a92020-04-21 14:33:20 -070018from cryptography import utils
Thea Flowerse290a3d2020-04-01 10:11:42 -070019import 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
arithmetic1728cf2c0a92020-04-21 14:33:20 -070025from cryptography.hazmat.primitives.asymmetric.utils import decode_dss_signature
26from cryptography.hazmat.primitives.asymmetric.utils import encode_dss_signature
Thea Flowerse290a3d2020-04-01 10:11:42 -070027import cryptography.x509
Thea Flowerse290a3d2020-04-01 10:11:42 -070028
29from google.auth import _helpers
30from google.auth.crypt import base
31
Thea Flowerse290a3d2020-04-01 10:11:42 -070032
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):
arithmetic1728cf2c0a92020-04-21 14:33:20 -070052 # 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 = utils.int_from_bytes(sig_bytes[:32], byteorder="big")
57 s = utils.int_from_bytes(sig_bytes[32:], byteorder="big")
58 asn1_sig = encode_dss_signature(r, s)
59
Thea Flowerse290a3d2020-04-01 10:11:42 -070060 message = _helpers.to_bytes(message)
61 try:
arithmetic1728cf2c0a92020-04-21 14:33:20 -070062 self._pubkey.verify(asn1_sig, message, ec.ECDSA(hashes.SHA256()))
Thea Flowerse290a3d2020-04-01 10:11:42 -070063 return True
64 except (ValueError, cryptography.exceptions.InvalidSignature):
65 return False
66
67 @classmethod
68 def from_string(cls, public_key):
69 """Construct an Verifier instance from a public key or public
70 certificate string.
71
72 Args:
73 public_key (Union[str, bytes]): The public key in PEM format or the
74 x509 public key certificate.
75
76 Returns:
77 Verifier: The constructed verifier.
78
79 Raises:
80 ValueError: If the public key can't be parsed.
81 """
82 public_key_data = _helpers.to_bytes(public_key)
83
84 if _CERTIFICATE_MARKER in public_key_data:
85 cert = cryptography.x509.load_pem_x509_certificate(
86 public_key_data, _BACKEND
87 )
88 pubkey = cert.public_key()
89
90 else:
91 pubkey = serialization.load_pem_public_key(public_key_data, _BACKEND)
92
93 return cls(pubkey)
94
95
96class ES256Signer(base.Signer, base.FromServiceAccountMixin):
97 """Signs messages with an ECDSA private key.
98
99 Args:
100 private_key (
101 cryptography.hazmat.primitives.asymmetric.ec.ECDSAPrivateKey):
102 The private key to sign with.
103 key_id (str): Optional key ID used to identify this private key. This
104 can be useful to associate the private key with its associated
105 public key or certificate.
106 """
107
108 def __init__(self, private_key, key_id=None):
109 self._key = private_key
110 self._key_id = key_id
111
112 @property
113 @_helpers.copy_docstring(base.Signer)
114 def key_id(self):
115 return self._key_id
116
117 @_helpers.copy_docstring(base.Signer)
118 def sign(self, message):
119 message = _helpers.to_bytes(message)
arithmetic1728cf2c0a92020-04-21 14:33:20 -0700120 asn1_signature = self._key.sign(message, ec.ECDSA(hashes.SHA256()))
121
122 # Convert ASN1 encoded signature to (r||s) raw signature.
123 (r, s) = decode_dss_signature(asn1_signature)
124 return utils.int_to_bytes(r, 32) + utils.int_to_bytes(s, 32)
Thea Flowerse290a3d2020-04-01 10:11:42 -0700125
126 @classmethod
127 def from_string(cls, key, key_id=None):
128 """Construct a RSASigner from a private key in PEM format.
129
130 Args:
131 key (Union[bytes, str]): Private key in PEM format.
132 key_id (str): An optional key id used to identify the private key.
133
134 Returns:
135 google.auth.crypt._cryptography_rsa.RSASigner: The
136 constructed signer.
137
138 Raises:
139 ValueError: If ``key`` is not ``bytes`` or ``str`` (unicode).
140 UnicodeDecodeError: If ``key`` is ``bytes`` but cannot be decoded
141 into a UTF-8 ``str``.
142 ValueError: If ``cryptography`` "Could not deserialize key data."
143 """
144 key = _helpers.to_bytes(key)
145 private_key = serialization.load_pem_private_key(
146 key, password=None, backend=_BACKEND
147 )
148 return cls(private_key, key_id=key_id)