blob: 71dcbfcac097e0a51a226b99201c8c4e73d7eafa [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
arithmetic1728cf2c0a92020-04-21 14:33:20 -070024from cryptography.hazmat.primitives.asymmetric.utils import decode_dss_signature
25from cryptography.hazmat.primitives.asymmetric.utils import encode_dss_signature
Thea Flowerse290a3d2020-04-01 10:11:42 -070026import cryptography.x509
Thea Flowerse290a3d2020-04-01 10:11:42 -070027
28from google.auth import _helpers
29from google.auth.crypt import base
30
Thea Flowerse290a3d2020-04-01 10:11:42 -070031
32_CERTIFICATE_MARKER = b"-----BEGIN CERTIFICATE-----"
33_BACKEND = backends.default_backend()
34_PADDING = padding.PKCS1v15()
35
36
37class ES256Verifier(base.Verifier):
38 """Verifies ECDSA cryptographic signatures using public keys.
39
40 Args:
41 public_key (
42 cryptography.hazmat.primitives.asymmetric.ec.ECDSAPublicKey):
43 The public key used to verify signatures.
44 """
45
46 def __init__(self, public_key):
47 self._pubkey = public_key
48
49 @_helpers.copy_docstring(base.Verifier)
50 def verify(self, message, signature):
arithmetic1728cf2c0a92020-04-21 14:33:20 -070051 # First convert (r||s) raw signature to ASN1 encoded signature.
52 sig_bytes = _helpers.to_bytes(signature)
53 if len(sig_bytes) != 64:
54 return False
Bu Sun Kim466aed92021-08-20 12:14:15 -060055 r = int.from_bytes(sig_bytes[:32], byteorder="big")
56 s = int.from_bytes(sig_bytes[32:], byteorder="big")
arithmetic1728cf2c0a92020-04-21 14:33:20 -070057 asn1_sig = encode_dss_signature(r, s)
58
Thea Flowerse290a3d2020-04-01 10:11:42 -070059 message = _helpers.to_bytes(message)
60 try:
arithmetic1728cf2c0a92020-04-21 14:33:20 -070061 self._pubkey.verify(asn1_sig, message, ec.ECDSA(hashes.SHA256()))
Thea Flowerse290a3d2020-04-01 10:11:42 -070062 return True
63 except (ValueError, cryptography.exceptions.InvalidSignature):
64 return False
65
66 @classmethod
67 def from_string(cls, public_key):
68 """Construct an Verifier instance from a public key or public
69 certificate string.
70
71 Args:
72 public_key (Union[str, bytes]): The public key in PEM format or the
73 x509 public key certificate.
74
75 Returns:
76 Verifier: The constructed verifier.
77
78 Raises:
79 ValueError: If the public key can't be parsed.
80 """
81 public_key_data = _helpers.to_bytes(public_key)
82
83 if _CERTIFICATE_MARKER in public_key_data:
84 cert = cryptography.x509.load_pem_x509_certificate(
85 public_key_data, _BACKEND
86 )
87 pubkey = cert.public_key()
88
89 else:
90 pubkey = serialization.load_pem_public_key(public_key_data, _BACKEND)
91
92 return cls(pubkey)
93
94
95class ES256Signer(base.Signer, base.FromServiceAccountMixin):
96 """Signs messages with an ECDSA private key.
97
98 Args:
99 private_key (
100 cryptography.hazmat.primitives.asymmetric.ec.ECDSAPrivateKey):
101 The private key to sign with.
102 key_id (str): Optional key ID used to identify this private key. This
103 can be useful to associate the private key with its associated
104 public key or certificate.
105 """
106
107 def __init__(self, private_key, key_id=None):
108 self._key = private_key
109 self._key_id = key_id
110
111 @property
112 @_helpers.copy_docstring(base.Signer)
113 def key_id(self):
114 return self._key_id
115
116 @_helpers.copy_docstring(base.Signer)
117 def sign(self, message):
118 message = _helpers.to_bytes(message)
arithmetic1728cf2c0a92020-04-21 14:33:20 -0700119 asn1_signature = self._key.sign(message, ec.ECDSA(hashes.SHA256()))
120
121 # Convert ASN1 encoded signature to (r||s) raw signature.
122 (r, s) = decode_dss_signature(asn1_signature)
Tres Seaverb79b5542021-08-23 14:24:28 -0400123 return r.to_bytes(32, byteorder="big") + s.to_bytes(32, byteorder="big")
Thea Flowerse290a3d2020-04-01 10:11:42 -0700124
125 @classmethod
126 def from_string(cls, key, key_id=None):
127 """Construct a RSASigner from a private key in PEM format.
128
129 Args:
130 key (Union[bytes, str]): Private key in PEM format.
131 key_id (str): An optional key id used to identify the private key.
132
133 Returns:
134 google.auth.crypt._cryptography_rsa.RSASigner: The
135 constructed signer.
136
137 Raises:
138 ValueError: If ``key`` is not ``bytes`` or ``str`` (unicode).
139 UnicodeDecodeError: If ``key`` is ``bytes`` but cannot be decoded
140 into a UTF-8 ``str``.
141 ValueError: If ``cryptography`` "Could not deserialize key data."
142 """
143 key = _helpers.to_bytes(key)
144 private_key = serialization.load_pem_private_key(
145 key, password=None, backend=_BACKEND
146 )
147 return cls(private_key, key_id=key_id)