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 15import json 16import os 17 18import mock 19from pyasn1_modules import pem 20import pytest 21import rsa 22import six 23 24from google.auth import _helpers 25from google.auth.crypt import _python_rsa 26from google.auth.crypt import base 27 28 29DATA_DIR = os.path.join(os.path.dirname(__file__), "..", "data") 30 31# To generate privatekey.pem, privatekey.pub, and public_cert.pem: 32# $ openssl req -new -newkey rsa:1024 -x509 -nodes -out public_cert.pem \ 33# > -keyout privatekey.pem 34# $ openssl rsa -in privatekey.pem -pubout -out privatekey.pub 35 36with open(os.path.join(DATA_DIR, "privatekey.pem"), "rb") as fh: 37 PRIVATE_KEY_BYTES = fh.read() 38 PKCS1_KEY_BYTES = PRIVATE_KEY_BYTES 39 40with open(os.path.join(DATA_DIR, "privatekey.pub"), "rb") as fh: 41 PUBLIC_KEY_BYTES = fh.read() 42 43with open(os.path.join(DATA_DIR, "public_cert.pem"), "rb") as fh: 44 PUBLIC_CERT_BYTES = fh.read() 45 46# To generate pem_from_pkcs12.pem and privatekey.p12: 47# $ openssl pkcs12 -export -out privatekey.p12 -inkey privatekey.pem \ 48# > -in public_cert.pem 49# $ openssl pkcs12 -in privatekey.p12 -nocerts -nodes \ 50# > -out pem_from_pkcs12.pem 51 52with open(os.path.join(DATA_DIR, "pem_from_pkcs12.pem"), "rb") as fh: 53 PKCS8_KEY_BYTES = fh.read() 54 55with open(os.path.join(DATA_DIR, "privatekey.p12"), "rb") as fh: 56 PKCS12_KEY_BYTES = fh.read() 57 58# The service account JSON file can be generated from the Google Cloud Console. 59SERVICE_ACCOUNT_JSON_FILE = os.path.join(DATA_DIR, "service_account.json") 60 61with open(SERVICE_ACCOUNT_JSON_FILE, "r") as fh: 62 SERVICE_ACCOUNT_INFO = json.load(fh) 63 64 65class TestRSAVerifier(object): 66 def test_verify_success(self): 67 to_sign = b"foo" 68 signer = _python_rsa.RSASigner.from_string(PRIVATE_KEY_BYTES) 69 actual_signature = signer.sign(to_sign) 70 71 verifier = _python_rsa.RSAVerifier.from_string(PUBLIC_KEY_BYTES) 72 assert verifier.verify(to_sign, actual_signature) 73 74 def test_verify_unicode_success(self): 75 to_sign = u"foo" 76 signer = _python_rsa.RSASigner.from_string(PRIVATE_KEY_BYTES) 77 actual_signature = signer.sign(to_sign) 78 79 verifier = _python_rsa.RSAVerifier.from_string(PUBLIC_KEY_BYTES) 80 assert verifier.verify(to_sign, actual_signature) 81 82 def test_verify_failure(self): 83 verifier = _python_rsa.RSAVerifier.from_string(PUBLIC_KEY_BYTES) 84 bad_signature1 = b"" 85 assert not verifier.verify(b"foo", bad_signature1) 86 bad_signature2 = b"a" 87 assert not verifier.verify(b"foo", bad_signature2) 88 89 def test_from_string_pub_key(self): 90 verifier = _python_rsa.RSAVerifier.from_string(PUBLIC_KEY_BYTES) 91 assert isinstance(verifier, _python_rsa.RSAVerifier) 92 assert isinstance(verifier._pubkey, rsa.key.PublicKey) 93 94 def test_from_string_pub_key_unicode(self): 95 public_key = _helpers.from_bytes(PUBLIC_KEY_BYTES) 96 verifier = _python_rsa.RSAVerifier.from_string(public_key) 97 assert isinstance(verifier, _python_rsa.RSAVerifier) 98 assert isinstance(verifier._pubkey, rsa.key.PublicKey) 99 100 def test_from_string_pub_cert(self): 101 verifier = _python_rsa.RSAVerifier.from_string(PUBLIC_CERT_BYTES) 102 assert isinstance(verifier, _python_rsa.RSAVerifier) 103 assert isinstance(verifier._pubkey, rsa.key.PublicKey) 104 105 def test_from_string_pub_cert_unicode(self): 106 public_cert = _helpers.from_bytes(PUBLIC_CERT_BYTES) 107 verifier = _python_rsa.RSAVerifier.from_string(public_cert) 108 assert isinstance(verifier, _python_rsa.RSAVerifier) 109 assert isinstance(verifier._pubkey, rsa.key.PublicKey) 110 111 def test_from_string_pub_cert_failure(self): 112 cert_bytes = PUBLIC_CERT_BYTES 113 true_der = rsa.pem.load_pem(cert_bytes, "CERTIFICATE") 114 load_pem_patch = mock.patch( 115 "rsa.pem.load_pem", return_value=true_der + b"extra", autospec=True 116 ) 117 118 with load_pem_patch as load_pem: 119 with pytest.raises(ValueError): 120 _python_rsa.RSAVerifier.from_string(cert_bytes) 121 load_pem.assert_called_once_with(cert_bytes, "CERTIFICATE") 122 123 124class TestRSASigner(object): 125 def test_from_string_pkcs1(self): 126 signer = _python_rsa.RSASigner.from_string(PKCS1_KEY_BYTES) 127 assert isinstance(signer, _python_rsa.RSASigner) 128 assert isinstance(signer._key, rsa.key.PrivateKey) 129 130 def test_from_string_pkcs1_unicode(self): 131 key_bytes = _helpers.from_bytes(PKCS1_KEY_BYTES) 132 signer = _python_rsa.RSASigner.from_string(key_bytes) 133 assert isinstance(signer, _python_rsa.RSASigner) 134 assert isinstance(signer._key, rsa.key.PrivateKey) 135 136 def test_from_string_pkcs8(self): 137 signer = _python_rsa.RSASigner.from_string(PKCS8_KEY_BYTES) 138 assert isinstance(signer, _python_rsa.RSASigner) 139 assert isinstance(signer._key, rsa.key.PrivateKey) 140 141 def test_from_string_pkcs8_extra_bytes(self): 142 key_bytes = PKCS8_KEY_BYTES 143 _, pem_bytes = pem.readPemBlocksFromFile( 144 six.StringIO(_helpers.from_bytes(key_bytes)), _python_rsa._PKCS8_MARKER 145 ) 146 147 key_info, remaining = None, "extra" 148 decode_patch = mock.patch( 149 "pyasn1.codec.der.decoder.decode", 150 return_value=(key_info, remaining), 151 autospec=True, 152 ) 153 154 with decode_patch as decode: 155 with pytest.raises(ValueError): 156 _python_rsa.RSASigner.from_string(key_bytes) 157 # Verify mock was called. 158 decode.assert_called_once_with(pem_bytes, asn1Spec=_python_rsa._PKCS8_SPEC) 159 160 def test_from_string_pkcs8_unicode(self): 161 key_bytes = _helpers.from_bytes(PKCS8_KEY_BYTES) 162 signer = _python_rsa.RSASigner.from_string(key_bytes) 163 assert isinstance(signer, _python_rsa.RSASigner) 164 assert isinstance(signer._key, rsa.key.PrivateKey) 165 166 def test_from_string_pkcs12(self): 167 with pytest.raises(ValueError): 168 _python_rsa.RSASigner.from_string(PKCS12_KEY_BYTES) 169 170 def test_from_string_bogus_key(self): 171 key_bytes = "bogus-key" 172 with pytest.raises(ValueError): 173 _python_rsa.RSASigner.from_string(key_bytes) 174 175 def test_from_service_account_info(self): 176 signer = _python_rsa.RSASigner.from_service_account_info(SERVICE_ACCOUNT_INFO) 177 178 assert signer.key_id == SERVICE_ACCOUNT_INFO[base._JSON_FILE_PRIVATE_KEY_ID] 179 assert isinstance(signer._key, rsa.key.PrivateKey) 180 181 def test_from_service_account_info_missing_key(self): 182 with pytest.raises(ValueError) as excinfo: 183 _python_rsa.RSASigner.from_service_account_info({}) 184 185 assert excinfo.match(base._JSON_FILE_PRIVATE_KEY) 186 187 def test_from_service_account_file(self): 188 signer = _python_rsa.RSASigner.from_service_account_file( 189 SERVICE_ACCOUNT_JSON_FILE 190 ) 191 192 assert signer.key_id == SERVICE_ACCOUNT_INFO[base._JSON_FILE_PRIVATE_KEY_ID] 193 assert isinstance(signer._key, rsa.key.PrivateKey) 194