1# Copyright 2021 The Pigweed Authors 2# 3# Licensed under the Apache License, Version 2.0 (the "License"); you may not 4# use this file except in compliance with the License. You may obtain a copy of 5# the License at 6# 7# https://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, WITHOUT 11# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 12# License for the specific language governing permissions and limitations under 13# the License. 14"""Unit tests for pw_software_update.remote_sign module.""" 15 16import os 17from pathlib import Path 18import tempfile 19import textwrap 20import unittest 21from unittest import mock 22 23from google.cloud.storage import Blob # type: ignore 24from google.cloud.storage.bucket import Bucket # type: ignore 25 26from pw_software_update import remote_sign 27 28# inclusive-language: disable 29FAKE_BUILDER_KEY = textwrap.dedent( 30 """\ 31 -----BEGIN RSA PRIVATE KEY----- 32 MIIEpAIBAAKCAQEA4qEQSHM0QpWEhTvhWMBahS7wbTIihaiRpUQC8+hEkmHhoJQy 33 zaNR3CKdYWnJ1bAjdBT1HTHznbYSBasFAUKPiB16K/akuKSPnwHG9OM6+8Psw7lt 34 GLP2jP65HE4a8n9lGas399xIK4hxZJkV2BXocociXVEVB3nzzNk1AQZdJxik/ToL 35 MYC2EKTu1kdt+OLl56/O1Mq9p8V7u2G1l8fqHtJi4Z34LzUzIoyFf7+bSmZBcHG1 36 F/QdjbHb4temShDzptOM1VfXZchYTDVnbNsmR7TP2B857agog4rhqtVlPvHqFial 37 WEU1WmAQz+oYqtRikUVWHq10SACxo6MFoM7LqQIDAQABAoIBAQCjrdoZyYLUGDLn 38 G1FtDTgTesxQwWXnjNDsQMu1J2rnImSX2pE6rhtAV4u9QG9ys01X2I8Tr/EYVdh8 39 WYE64LzTfR6ww+lCJjBIkjsEwVznWyEUV0bxEYEfYhWF2O9jdxkoyd2ZWXKSZnAn 40 TN1W/LOui+UI6re6d5zatYGvpM4AnlMTmwcO5aPQqTZMBOqJQZgEgyyHH2DZpIRI 41 L7dG/k9Y/ML4T+hSVvi84+NS7GyTajPtaNRoVnlwr9+QVKplIgT8ZSqAF7unBsmF 42 +s/U+TCFKq0pOhamOVz8eVd/uusy0d7a2oomtKoIzcPd74J5KZMub8izmEOwp5TZ 43 17rsBDuVAoGBAPJMv737tYf5T5ihwEJ84OWxq3qLwWSoNwezOF3iWi4r42+QoqsC 44 F0dLlgTmsafNTwVP1ztoeGvvezXSUfKfMTjjaZDRB226gwW7+eZ0MbrVcEnI9wm5 45 K9MOWut40KsoAHHGs5sLyAtIENwnPAkPQPwPxmEcUxJJZwI/Rq78zR1bAoGBAO9x 46 fAi9M8VdbV3r/l1SnExRlTu2gp9Rv02Zy78HVOWEWdEn6mhG3to5mXsHNIQwLulQ 47 jm/hBSme5g4xbSCL/qCqRkc1rRautq/W50G8h7S3+KFGdnzXdEYnEh4oh5t5PJtA 48 LHWc2WhTe5bsBNwOR1xQ8bEm+V/lf4Fbq+jOcjZLAoGBALzKgDwPfApOf2512c/0 49 bWeLYAlEC5PaXcZqJmlAjPOczsGG+Lg2EN1ET8fR2Gre1ctVwmZPqESxfFcbYS6i 50 S0AAMajcteURhjVZmgWuU3E4DR3wsEurNDJm5QDEShKSQIZmRFtyepQPutNO3sBQ 51 WloMEI5p+3AsMU7W7sQ5xbgxAoGABWwAbwI5xeJTs6jAXcSdHW1Lf8qmMo1bU5qD 52 7pNv7LKOhhntSOcx7KcZPpvvKH8e0NGuKAJkZ4jdlLyxx+bjoSe556rjfHwATwMC 53 wY5PVFxGGQDLdhA65cvEsUIhr/eS08EkQJWIpsAdMFGv2nvISeLbVjOXugAsXvWA 54 cwkZtPkCgYBQJPRCGBz73lBFB16oYJR6AnC32nf0WbrnMLFwMejUrsTLN6M72axw 55 bWFpW7rWV7ldSgxKJ6ucKWl78uUMUnkM+CGPt5WJiisZXM2X1Q8+V7fmdAtK/AFj 56 /tbEFpftkCyIM1nGZwZ/ziPF4n5hzGGF6w/ZMWZkwFZlqxlejK9IfQ== 57 -----END RSA PRIVATE KEY----- 58""" 59) 60 61FAKE_BUILDER_PUBLIC_KEY = textwrap.dedent( 62 """\ 63 -----BEGIN PUBLIC KEY----- 64 MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA4qEQSHM0QpWEhTvhWMBa 65 hS7wbTIihaiRpUQC8+hEkmHhoJQyzaNR3CKdYWnJ1bAjdBT1HTHznbYSBasFAUKP 66 iB16K/akuKSPnwHG9OM6+8Psw7ltGLP2jP65HE4a8n9lGas399xIK4hxZJkV2BXo 67 cociXVEVB3nzzNk1AQZdJxik/ToLMYC2EKTu1kdt+OLl56/O1Mq9p8V7u2G1l8fq 68 HtJi4Z34LzUzIoyFf7+bSmZBcHG1F/QdjbHb4temShDzptOM1VfXZchYTDVnbNsm 69 R7TP2B857agog4rhqtVlPvHqFialWEU1WmAQz+oYqtRikUVWHq10SACxo6MFoM7L 70 qQIDAQAB 71 -----END PUBLIC KEY----- 72""" 73) 74 75# inclusive-language: enable 76 77 78# TODO: b/235240430 - Improve unit test coverage. 79class PathSigningTest(unittest.TestCase): 80 """Tests the signing of bundles by path.""" 81 82 @classmethod 83 def setUpClass(cls): 84 cls.tempdir = tempfile.mkdtemp() 85 86 cls.builder_key = Path(cls.tempdir) / 'fake_builder_key' 87 cls.builder_key.write_text(FAKE_BUILDER_KEY) 88 89 cls.builder_pub_key = Path(cls.tempdir) / 'fake_builder_pub_key.pem' 90 cls.builder_pub_key.write_text(FAKE_BUILDER_PUBLIC_KEY) 91 92 cls.bundle = Path(cls.tempdir) / 'fake_bundle' 93 cls.bundle.write_bytes(b'FAKE BUNDLE CONTENTS\n') 94 95 def test_bundle_blob_uploads(self): 96 """Signing should upload the bundle, pub key, and signing request.""" 97 mock_blob = mock.create_autospec(Blob, instance=True) 98 mock_blob.exists = mock.MagicMock(return_value=False) 99 mock_in_bucket = mock.create_autospec(Bucket, instance=True) 100 mock_in_bucket.blob = mock.MagicMock(return_value=mock_blob) 101 mock_out_bucket = mock.create_autospec(Bucket, instance=True) 102 mock_out_bucket.name = 'fake_out_bucket' 103 client = remote_sign.RemoteSignClient( 104 input_bucket=mock_in_bucket, output_bucket=mock_out_bucket 105 ) 106 107 client.sign( 108 self.bundle, 109 signing_key_name='fake_key', 110 builder_key=self.builder_key, 111 builder_public_key=self.builder_pub_key, 112 request_blob_name='signing_request.json', 113 timeout_s=0.1, 114 ) 115 116 mock_blob.upload_from_filename.assert_has_calls( 117 [ 118 mock.call(os.path.join(self.tempdir, 'fake_bundle')), 119 mock.call( 120 os.path.join(self.tempdir, 'fake_builder_pub_key.pem') 121 ), 122 ], 123 any_order=True, 124 ) 125 mock_blob.upload_from_string.assert_called_once() 126 127 128if __name__ == '__main__': 129 unittest.main() 130