blob: 9671230c622dadec25c101e0d232a503058d4063 [file] [log] [blame]
Jon Wayne Parrott8713a712016-10-04 14:19:01 -07001# Copyright 2016 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
Jon Wayne Parrottfc169292016-12-13 16:02:40 -080015import json
Jon Wayne Parrott54451fb2016-12-14 12:20:24 -080016import os
Jon Wayne Parrott8713a712016-10-04 14:19:01 -070017
18import mock
19from pyasn1_modules import pem
20import pytest
21import rsa
22import six
23
24from google.auth import _helpers
25from google.auth import crypt
26
27
28DATA_DIR = os.path.join(os.path.dirname(__file__), 'data')
29
30# To generate privatekey.pem, privatekey.pub, and public_cert.pem:
31# $ openssl req -new -newkey rsa:1024 -x509 -nodes -out public_cert.pem \
32# > -keyout privatekey.pem
33# $ openssl rsa -in privatekey.pem -pubout -out privatekey.pub
34
35with open(os.path.join(DATA_DIR, 'privatekey.pem'), 'rb') as fh:
36 PRIVATE_KEY_BYTES = fh.read()
37 PKCS1_KEY_BYTES = PRIVATE_KEY_BYTES
38
39with open(os.path.join(DATA_DIR, 'privatekey.pub'), 'rb') as fh:
40 PUBLIC_KEY_BYTES = fh.read()
41
42with open(os.path.join(DATA_DIR, 'public_cert.pem'), 'rb') as fh:
43 PUBLIC_CERT_BYTES = fh.read()
44
45# To generate other_cert.pem:
46# $ openssl req -new -newkey rsa:1024 -x509 -nodes -out other_cert.pem
47
48with open(os.path.join(DATA_DIR, 'other_cert.pem'), 'rb') as fh:
49 OTHER_CERT_BYTES = fh.read()
50
51# To generate pem_from_pkcs12.pem and privatekey.p12:
52# $ openssl pkcs12 -export -out privatekey.p12 -inkey privatekey.pem \
53# > -in public_cert.pem
54# $ openssl pkcs12 -in privatekey.p12 -nocerts -nodes \
55# > -out pem_from_pkcs12.pem
56
57with open(os.path.join(DATA_DIR, 'pem_from_pkcs12.pem'), 'rb') as fh:
58 PKCS8_KEY_BYTES = fh.read()
59
60with open(os.path.join(DATA_DIR, 'privatekey.p12'), 'rb') as fh:
61 PKCS12_KEY_BYTES = fh.read()
62
Jon Wayne Parrottfc169292016-12-13 16:02:40 -080063# The service account JSON file can be generated from the Google Cloud Console.
64SERVICE_ACCOUNT_JSON_FILE = os.path.join(DATA_DIR, 'service_account.json')
65
66with open(SERVICE_ACCOUNT_JSON_FILE, 'r') as fh:
67 SERVICE_ACCOUNT_INFO = json.load(fh)
68
Jon Wayne Parrott8713a712016-10-04 14:19:01 -070069
70def test_verify_signature():
71 to_sign = b'foo'
72 signer = crypt.Signer.from_string(PRIVATE_KEY_BYTES)
73 signature = signer.sign(to_sign)
74
75 assert crypt.verify_signature(
76 to_sign, signature, PUBLIC_CERT_BYTES)
77
78 # List of certs
79 assert crypt.verify_signature(
80 to_sign, signature, [OTHER_CERT_BYTES, PUBLIC_CERT_BYTES])
81
82
83def test_verify_signature_failure():
84 to_sign = b'foo'
85 signer = crypt.Signer.from_string(PRIVATE_KEY_BYTES)
86 signature = signer.sign(to_sign)
87
88 assert not crypt.verify_signature(
89 to_sign, signature, OTHER_CERT_BYTES)
90
91
92class TestVerifier(object):
93 def test_verify_success(self):
94 to_sign = b'foo'
95 signer = crypt.Signer.from_string(PRIVATE_KEY_BYTES)
96 actual_signature = signer.sign(to_sign)
97
98 verifier = crypt.Verifier.from_string(PUBLIC_KEY_BYTES)
99 assert verifier.verify(to_sign, actual_signature)
100
101 def test_verify_unicode_success(self):
102 to_sign = u'foo'
103 signer = crypt.Signer.from_string(PRIVATE_KEY_BYTES)
104 actual_signature = signer.sign(to_sign)
105
106 verifier = crypt.Verifier.from_string(PUBLIC_KEY_BYTES)
107 assert verifier.verify(to_sign, actual_signature)
108
109 def test_verify_failure(self):
110 verifier = crypt.Verifier.from_string(PUBLIC_KEY_BYTES)
111 bad_signature1 = b''
112 assert not verifier.verify(b'foo', bad_signature1)
113 bad_signature2 = b'a'
114 assert not verifier.verify(b'foo', bad_signature2)
115
116 def test_from_string_pub_key(self):
117 verifier = crypt.Verifier.from_string(PUBLIC_KEY_BYTES)
118 assert isinstance(verifier, crypt.Verifier)
119 assert isinstance(verifier._pubkey, rsa.key.PublicKey)
120
121 def test_from_string_pub_key_unicode(self):
122 public_key = _helpers.from_bytes(PUBLIC_KEY_BYTES)
123 verifier = crypt.Verifier.from_string(public_key)
124 assert isinstance(verifier, crypt.Verifier)
125 assert isinstance(verifier._pubkey, rsa.key.PublicKey)
126
127 def test_from_string_pub_cert(self):
128 verifier = crypt.Verifier.from_string(PUBLIC_CERT_BYTES)
129 assert isinstance(verifier, crypt.Verifier)
130 assert isinstance(verifier._pubkey, rsa.key.PublicKey)
131
132 def test_from_string_pub_cert_unicode(self):
133 public_cert = _helpers.from_bytes(PUBLIC_CERT_BYTES)
134 verifier = crypt.Verifier.from_string(public_cert)
135 assert isinstance(verifier, crypt.Verifier)
136 assert isinstance(verifier._pubkey, rsa.key.PublicKey)
137
138 def test_from_string_pub_cert_failure(self):
139 cert_bytes = PUBLIC_CERT_BYTES
140 true_der = rsa.pem.load_pem(cert_bytes, 'CERTIFICATE')
Jon Wayne Parrott8784b232016-11-10 12:53:55 -0800141 load_pem_patch = mock.patch(
142 'rsa.pem.load_pem', return_value=true_der + b'extra',
143 autospec=True)
144
145 with load_pem_patch as load_pem:
Jon Wayne Parrott8713a712016-10-04 14:19:01 -0700146 with pytest.raises(ValueError):
147 crypt.Verifier.from_string(cert_bytes)
148 load_pem.assert_called_once_with(cert_bytes, 'CERTIFICATE')
149
150
151class TestSigner(object):
152 def test_from_string_pkcs1(self):
153 signer = crypt.Signer.from_string(PKCS1_KEY_BYTES)
154 assert isinstance(signer, crypt.Signer)
155 assert isinstance(signer._key, rsa.key.PrivateKey)
156
157 def test_from_string_pkcs1_unicode(self):
158 key_bytes = _helpers.from_bytes(PKCS1_KEY_BYTES)
159 signer = crypt.Signer.from_string(key_bytes)
160 assert isinstance(signer, crypt.Signer)
161 assert isinstance(signer._key, rsa.key.PrivateKey)
162
163 def test_from_string_pkcs8(self):
164 signer = crypt.Signer.from_string(PKCS8_KEY_BYTES)
165 assert isinstance(signer, crypt.Signer)
166 assert isinstance(signer._key, rsa.key.PrivateKey)
167
168 def test_from_string_pkcs8_extra_bytes(self):
169 key_bytes = PKCS8_KEY_BYTES
170 _, pem_bytes = pem.readPemBlocksFromFile(
171 six.StringIO(_helpers.from_bytes(key_bytes)),
172 crypt._PKCS8_MARKER)
173
Jon Wayne Parrott8784b232016-11-10 12:53:55 -0800174 key_info, remaining = None, 'extra'
175 decode_patch = mock.patch(
176 'pyasn1.codec.der.decoder.decode',
177 return_value=(key_info, remaining),
178 autospec=True)
179
180 with decode_patch as decode:
Jon Wayne Parrott8713a712016-10-04 14:19:01 -0700181 with pytest.raises(ValueError):
182 crypt.Signer.from_string(key_bytes)
183 # Verify mock was called.
Jon Wayne Parrott8784b232016-11-10 12:53:55 -0800184 decode.assert_called_once_with(
Jon Wayne Parrott8713a712016-10-04 14:19:01 -0700185 pem_bytes, asn1Spec=crypt._PKCS8_SPEC)
186
187 def test_from_string_pkcs8_unicode(self):
188 key_bytes = _helpers.from_bytes(PKCS8_KEY_BYTES)
189 signer = crypt.Signer.from_string(key_bytes)
190 assert isinstance(signer, crypt.Signer)
191 assert isinstance(signer._key, rsa.key.PrivateKey)
192
193 def test_from_string_pkcs12(self):
194 with pytest.raises(ValueError):
195 crypt.Signer.from_string(PKCS12_KEY_BYTES)
196
197 def test_from_string_bogus_key(self):
198 key_bytes = 'bogus-key'
199 with pytest.raises(ValueError):
200 crypt.Signer.from_string(key_bytes)
Jon Wayne Parrottfc169292016-12-13 16:02:40 -0800201
Jon Wayne Parrott54451fb2016-12-14 12:20:24 -0800202 def test_from_service_account_info(self):
203 signer = crypt.Signer.from_service_account_info(SERVICE_ACCOUNT_INFO)
204
205 assert signer.key_id == SERVICE_ACCOUNT_INFO[
206 crypt._JSON_FILE_PRIVATE_KEY_ID]
207 assert isinstance(signer._key, rsa.key.PrivateKey)
208
209 def test_from_service_account_info_missing_key(self):
210 with pytest.raises(ValueError) as excinfo:
211 crypt.Signer.from_service_account_info({})
212
213 assert excinfo.match(crypt._JSON_FILE_PRIVATE_KEY)
214
Jon Wayne Parrottfc169292016-12-13 16:02:40 -0800215 def test_from_service_account_file(self):
216 signer = crypt.Signer.from_service_account_file(
217 SERVICE_ACCOUNT_JSON_FILE)
218
Jon Wayne Parrott54451fb2016-12-14 12:20:24 -0800219 assert signer.key_id == SERVICE_ACCOUNT_INFO[
220 crypt._JSON_FILE_PRIVATE_KEY_ID]
Jon Wayne Parrottfc169292016-12-13 16:02:40 -0800221 assert isinstance(signer._key, rsa.key.PrivateKey)