1# Copyright 2016 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"""Pure-Python RSA cryptography implementation.
16
17Uses the ``rsa``, ``pyasn1`` and ``pyasn1_modules`` packages
18to parse PEM files storing PKCS#1 or PKCS#8 keys as well as
19certificates. There is no support for p12 files.
20"""
21
22from __future__ import absolute_import
23
24import io
25import warnings
26
27from pyasn1.codec.der import decoder # type: ignore
28from pyasn1_modules import pem # type: ignore
29from pyasn1_modules.rfc2459 import Certificate # type: ignore
30from pyasn1_modules.rfc5208 import PrivateKeyInfo # type: ignore
31import rsa # type: ignore
32
33from google.auth import _helpers
34from google.auth import exceptions
35from google.auth.crypt import base
36
37_POW2 = (128, 64, 32, 16, 8, 4, 2, 1)
38_CERTIFICATE_MARKER = b"-----BEGIN CERTIFICATE-----"
39_PKCS1_MARKER = ("-----BEGIN RSA PRIVATE KEY-----", "-----END RSA PRIVATE KEY-----")
40_PKCS8_MARKER = ("-----BEGIN PRIVATE KEY-----", "-----END PRIVATE KEY-----")
41_PKCS8_SPEC = PrivateKeyInfo()
42
43_warning_msg = (
44 "The 'rsa' library is deprecated and will be removed in a future release. "
45 "Please migrate to 'cryptography'."
46)
47
48
49def _bit_list_to_bytes(bit_list):
50 """Converts an iterable of 1s and 0s to bytes.
51
52 Combines the list 8 at a time, treating each group of 8 bits
53 as a single byte.
54
55 Args:
56 bit_list (Sequence): Sequence of 1s and 0s.
57
58 Returns:
59 bytes: The decoded bytes.
60 """
61 num_bits = len(bit_list)
62 byte_vals = bytearray()
63 for start in range(0, num_bits, 8):
64 curr_bits = bit_list[start : start + 8]
65 char_val = sum(val * digit for val, digit in zip(_POW2, curr_bits))
66 byte_vals.append(char_val)
67 return bytes(byte_vals)
68
69
70class RSAVerifier(base.Verifier):
71 """Verifies RSA cryptographic signatures using public keys.
72
73 .. deprecated::
74 The `rsa` library has been archived. Please migrate to
75 `cryptography`.
76
77 Args:
78 public_key (rsa.key.PublicKey): The public key used to verify
79 signatures.
80 """
81
82 def __init__(self, public_key):
83 warnings.warn(
84 _warning_msg,
85 category=DeprecationWarning,
86 stacklevel=2,
87 )
88 self._pubkey = public_key
89
90 @_helpers.copy_docstring(base.Verifier)
91 def verify(self, message, signature):
92 message = _helpers.to_bytes(message)
93 try:
94 return rsa.pkcs1.verify(message, signature, self._pubkey)
95 except (ValueError, rsa.pkcs1.VerificationError):
96 return False
97
98 @classmethod
99 def from_string(cls, public_key):
100 """Construct an Verifier instance from a public key or public
101 certificate string.
102
103 Args:
104 public_key (Union[str, bytes]): The public key in PEM format or the
105 x509 public key certificate.
106
107 Returns:
108 google.auth.crypt._python_rsa.RSAVerifier: The constructed verifier.
109
110 Raises:
111 ValueError: If the public_key can't be parsed.
112 """
113 public_key = _helpers.to_bytes(public_key)
114 is_x509_cert = _CERTIFICATE_MARKER in public_key
115
116 # If this is a certificate, extract the public key info.
117 if is_x509_cert:
118 der = rsa.pem.load_pem(public_key, "CERTIFICATE")
119 asn1_cert, remaining = decoder.decode(der, asn1Spec=Certificate())
120 if remaining != b"":
121 raise exceptions.InvalidValue("Unused bytes", remaining)
122
123 cert_info = asn1_cert["tbsCertificate"]["subjectPublicKeyInfo"]
124 key_bytes = _bit_list_to_bytes(cert_info["subjectPublicKey"])
125 pubkey = rsa.PublicKey.load_pkcs1(key_bytes, "DER")
126 else:
127 pubkey = rsa.PublicKey.load_pkcs1(public_key, "PEM")
128 return cls(pubkey)
129
130
131class RSASigner(base.Signer, base.FromServiceAccountMixin):
132 """Signs messages with an RSA private key.
133
134 .. deprecated::
135 The `rsa` library has been archived. Please migrate to
136 `cryptography`.
137
138 Args:
139 private_key (rsa.key.PrivateKey): The private key to sign with.
140 key_id (str): Optional key ID used to identify this private key. This
141 can be useful to associate the private key with its associated
142 public key or certificate.
143 """
144
145 def __init__(self, private_key, key_id=None):
146 warnings.warn(
147 _warning_msg,
148 category=DeprecationWarning,
149 stacklevel=2,
150 )
151 self._key = private_key
152 self._key_id = key_id
153
154 @property # type: ignore
155 @_helpers.copy_docstring(base.Signer)
156 def key_id(self):
157 return self._key_id
158
159 @_helpers.copy_docstring(base.Signer)
160 def sign(self, message):
161 message = _helpers.to_bytes(message)
162 return rsa.pkcs1.sign(message, self._key, "SHA-256")
163
164 @classmethod
165 def from_string(cls, key, key_id=None):
166 """Construct an Signer instance from a private key in PEM format.
167
168 Args:
169 key (str): Private key in PEM format.
170 key_id (str): An optional key id used to identify the private key.
171
172 Returns:
173 google.auth.crypt.Signer: The constructed signer.
174
175 Raises:
176 ValueError: If the key cannot be parsed as PKCS#1 or PKCS#8 in
177 PEM format.
178 """
179 key = _helpers.from_bytes(key) # PEM expects str in Python 3
180 marker_id, key_bytes = pem.readPemBlocksFromFile(
181 io.StringIO(key), _PKCS1_MARKER, _PKCS8_MARKER
182 )
183
184 # Key is in pkcs1 format.
185 if marker_id == 0:
186 private_key = rsa.key.PrivateKey.load_pkcs1(key_bytes, format="DER")
187 # Key is in pkcs8.
188 elif marker_id == 1:
189 key_info, remaining = decoder.decode(key_bytes, asn1Spec=_PKCS8_SPEC)
190 if remaining != b"":
191 raise exceptions.InvalidValue("Unused bytes", remaining)
192 private_key_info = key_info.getComponentByName("privateKey")
193 private_key = rsa.key.PrivateKey.load_pkcs1(
194 private_key_info.asOctets(), format="DER"
195 )
196 else:
197 raise exceptions.MalformedError("No key could be detected.")
198
199 return cls(private_key, key_id=key_id)