blob: 621d0f3e2584f5fbf37c729c4e00477cec532f7a [file] [log] [blame]
Dan Austin715f5032020-01-08 10:18:05 -08001#!/usr/bin/env python
2
3# Copyright 2019, The Android Open Source Project
4#
5# Permission is hereby granted, free of charge, to any person
6# obtaining a copy of this software and associated documentation
7# files (the "Software"), to deal in the Software without
8# restriction, including without limitation the rights to use, copy,
9# modify, merge, publish, distribute, sublicense, and/or sell copies
10# of the Software, and to permit persons to whom the Software is
11# furnished to do so, subject to the following conditions:
12#
13# The above copyright notice and this permission notice shall be
14# included in all copies or substantial portions of the Software.
15#
16# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
20# BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
21# ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
22# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
23# SOFTWARE.
24#
25"""Unit tests for aftltool."""
26
27# pylint: disable=unused-import
28from __future__ import print_function
29
Jan Monsch6990f5d2020-02-12 18:25:51 +010030import base64
Dan Austin715f5032020-01-08 10:18:05 -080031import binascii
Jan Monsch58be6b32020-01-29 17:13:02 +010032import io
Dan Austin715f5032020-01-08 10:18:05 -080033import os
34import sys
35import unittest
36
37import aftltool
Jan Monsch58be6b32020-01-29 17:13:02 +010038import avbtool
Jan Monsch0ec6b9a2020-01-29 14:38:46 +010039import proto.aftl_pb2
40import proto.api_pb2
41import proto.trillian_pb2
42
Dan Austin715f5032020-01-08 10:18:05 -080043
Jan Monsch1b47ade2020-02-17 14:44:47 +010044# Workaround for b/149307145 in order to pick up the test data from the right
45# location independent where the script is called from.
46# TODO(b/149307145): Remove workaround once the referenced bug is fixed.
Jan Monsch443bf322020-02-19 14:56:44 +010047TEST_EXEC_PATH = os.path.dirname(os.path.realpath(__file__))
Jan Monsch1b47ade2020-02-17 14:44:47 +010048
49
Dan Austin715f5032020-01-08 10:18:05 -080050class AftltoolTestCase(unittest.TestCase):
51
52 def setUp(self):
53 """Sets up the test bed for the unit tests."""
54 super(AftltoolTestCase, self).setUp()
55
56 # Redirects the stderr to /dev/null when running the unittests. The reason
57 # is that soong interprets any output on stderr as an error and marks the
58 # unit test as failed although the test itself succeeded.
59 self.stderr = sys.stderr
60 self.null = open(os.devnull, 'wb')
61 sys.stderr = self.null
62
Jan Monsch90c7e582020-02-13 16:56:56 +010063 # AFTL public key.
64 self.test_aftl_pub_key = (
65 '-----BEGIN PUBLIC KEY-----\n'
66 'MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEA4ilqCNsenNA013iCdwgD\n'
67 'YPxZ853nbHG9lMBp9boXiwRcqT/8bUKHIL7YX5z7s+QoRYVY3rkMKppRabclXzyx\n'
68 'H59YnPMaU4uv7NqwWzjgaZo7E+vo7IF+KBjV3cJulId5Av0yIYUCsrwd7MpGtWdC\n'
69 'Q3S+7Vd4zwzCKEhcvliNIhnNlp1U3wNkPCxOyCAsMEn6k8O5ar12ke5TvxDv15db\n'
70 'rPDeHh8G2OYWoCkWL+lSN35L2kOJqKqVbLKWrrOd96RCYrrtbPCi580OADJRcUlG\n'
71 'lgcjwmNwmypBWvQMZ6ITj0P0ksHnl1zZz1DE2rXe1goLI1doghb5KxLaezlR8c2C\n'
72 'E3w/uo9KJgNmNgUVzzqZZ6FE0moyIDNOpP7KtZAL0DvEZj6jqLbB0ccPQElrg52m\n'
73 'Dv2/A3nYSr0mYBKeskT4+Bg7PGgoC8p7WyLSxMyzJEDYdtrj9OFx6eZaA23oqTQx\n'
74 'k3Qq5H8RfNBeeSUEeKF7pKH/7gyqZ2bNzBFMA2EBZgBozwRfaeN/HCv3qbaCnwvu\n'
75 '6caacmAsK+RxiYxSL1QsJqyhCWWGxVyenmxdc1KG/u5ypi7OIioztyzR3t2tAzD3\n'
76 'Nb+2t8lgHBRxbV24yiPlnvPmB1ZYEctXnlRR9Evpl1o9xA9NnybPHKr9rozN39CZ\n'
77 'V/USB8K6ao1y5xPZxa8CZksCAwEAAQ==\n'
78 '-----END PUBLIC KEY-----\n')
79
Jan Monsch1f58eec2020-02-10 17:05:15 +010080 # Test AftlIcpEntry #1
81 self.test_tl_url_1 = 'aftl-test-server.google.com'
82
83 self.test_sth_1 = aftltool.TrillianLogRootDescriptor()
84 self.test_sth_1.tree_size = 2
85 self.test_sth_1.root_hash_size = 32
86 self.test_sth_1.root_hash = bytearray('f' * 32)
87 self.test_sth_1.timestamp = 0x1234567890ABCDEF
88 self.test_sth_1.revision = 0xFEDCBA0987654321
89
90 self.test_sth_1_bytes = bytearray(
91 '\x00\x01' # version
92 '\x00\x00\x00\x00\x00\x00\x00\x02' # tree_size
93 '\x20' # root_hash_size
94 + 'f' * 32 + # root_hash
95 '\x12\x34\x56\x78\x90\xAB\xCD\xEF' # timestamp
96 '\xFE\xDC\xBA\x09\x87\x65\x43\x21' # revision
97 '\x00\x00' # metadata_size
98 '' # metadata (empty)
99 )
100
101 # Fill each structure with an easily observable pattern for easy validation.
102 self.test_proof_hashes_1 = []
103 self.test_proof_hashes_1.append(bytearray('b' * 32))
104 self.test_proof_hashes_1.append(bytearray('c' * 32))
105 self.test_proof_hashes_1.append(bytearray('d' * 32))
106 self.test_proof_hashes_1.append(bytearray('e' * 32))
107
108 # Valid test AftlIcpEntry #1.
109 self.test_entry_1 = aftltool.AftlIcpEntry()
Jan Monsch27905072020-02-11 15:40:32 +0100110 self.test_entry_1.log_url = self.test_tl_url_1
Jan Monsch1f58eec2020-02-10 17:05:15 +0100111 self.test_entry_1.leaf_index = 1
Jan Monsch27905072020-02-11 15:40:32 +0100112 self.test_entry_1.log_root_descriptor = self.test_sth_1
113 self.test_entry_1.proofs = self.test_proof_hashes_1
Jan Monsch1f58eec2020-02-10 17:05:15 +0100114 self.test_entry_1.log_root_signature = 'g' * 512 # bytearray('g' * 512)
Jan Monsch1f58eec2020-02-10 17:05:15 +0100115
116 self.test_entry_1_bytes = bytearray(
117 '\x00\x00\x00\x1b' # Transparency log url size.
118 '\x00\x00\x00\x00\x00\x00\x00\x01' # Leaf index.
119 '\x00\x00\x00\x3d' # Log root descriptor size.
120 '\x00\x00\x00\x00' # Firmware info leaf size.
121 '\x02\x00' # Log root signature size.
122 '\x04' # Number of hashes in ICP.
123 '\x00\x00\x00\x80' # Size of ICP in bytes.
Jan Monsch27905072020-02-11 15:40:32 +0100124 + self.test_tl_url_1 # Transparency log url.
Jan Monsch1f58eec2020-02-10 17:05:15 +0100125 + self.test_sth_1_bytes
126 + 'g' * 512 # Log root signature.
127 + 'b' * 32 # Hashes...
128 + 'c' * 32
129 + 'd' * 32
130 + 'e' * 32)
131
132 # Valid test AftlIcpEntry #2.
133 self.test_tl_url_2 = 'aftl-test-server.google.ch'
134
135 self.test_sth_2 = aftltool.TrillianLogRootDescriptor()
136 self.test_sth_2.tree_size = 4
137 self.test_sth_2.root_hash_size = 32
138 self.test_sth_2.root_hash = bytearray('e' * 32)
139 self.test_sth_2.timestamp = 6
140 self.test_sth_2.revision = 7
141 self.test_sth_2.metadata_size = 2
142 self.test_sth_2.metadata = '12'
143
144 self.test_sth_2_bytes = bytearray(
145 '\x00\x01' # version
146 '\x00\x00\x00\x00\x00\x00\x00\x04' # tree_size
147 '\x20' # root_hash_size
148 + 'e' * 32 + # root_hash
149 '\x00\x00\x00\x00\x00\x00\x00\x06' # timestamp
150 '\x00\x00\x00\x00\x00\x00\x00\x07' # revision
151 '\x00\x02' # metadata_size
152 '12' # metadata
153 )
154
155 # Fill each structure with an easily observable pattern for easy validation.
156 self.test_proof_hashes_2 = []
157 self.test_proof_hashes_2.append(bytearray('g' * 32))
158 self.test_proof_hashes_2.append(bytearray('h' * 32))
159
160 self.test_entry_2 = aftltool.AftlIcpEntry()
Jan Monsch27905072020-02-11 15:40:32 +0100161 self.test_entry_2.log_url = self.test_tl_url_2
Jan Monsch1f58eec2020-02-10 17:05:15 +0100162 self.test_entry_2.leaf_index = 2
Jan Monsch27905072020-02-11 15:40:32 +0100163 self.test_entry_2.log_root_descriptor = self.test_sth_2
Jan Monsch1f58eec2020-02-10 17:05:15 +0100164 self.test_entry_2.log_root_signature = bytearray('d' * 512)
Jan Monsch27905072020-02-11 15:40:32 +0100165 self.test_entry_2.proofs = self.test_proof_hashes_2
Jan Monsch1f58eec2020-02-10 17:05:15 +0100166
167 self.test_entry_2_bytes = bytearray(
168 '\x00\x00\x00\x1a' # Transparency log url size.
169 '\x00\x00\x00\x00\x00\x00\x00\x02' # Leaf index.
Jan Monsch27905072020-02-11 15:40:32 +0100170 '\x00\x00\x00\x3f' # Log root descriptor size.
Jan Monsch1f58eec2020-02-10 17:05:15 +0100171 '\x00\x00\x00\x00' # Firmware info leaf size.
172 '\x02\x00' # Log root signature size.
173 '\x02' # Number of hashes in ICP.
Jan Monsch27905072020-02-11 15:40:32 +0100174 '\x00\x00\x00\x40' # Size of ICP in bytes.
175 + self.test_tl_url_2 # Transparency log url.
Jan Monsch1f58eec2020-02-10 17:05:15 +0100176 + self.test_sth_2_bytes # Log root
177 + 'd' * 512 # Log root signature.
178 + 'g' * 32 # Hashes...
179 + 'h' * 32)
180
181 # Valid test AftlDescriptor made out of AftlEntry #1 and #2.
182 self.test_aftl_desc = aftltool.AftlDescriptor()
183 self.test_aftl_desc.add_icp_entry(self.test_entry_1)
184 self.test_aftl_desc.add_icp_entry(self.test_entry_2)
185
186 self.test_expected_aftl_descriptor_bytes = bytearray(
Jan Monsch1f58eec2020-02-10 17:05:15 +0100187 'AFTL' # Magic.
Jan Monsch1f58eec2020-02-10 17:05:15 +0100188 '\x00\x00\x00\x01' # Major version.
Jan Monsch6990f5d2020-02-12 18:25:51 +0100189 '\x00\x00\x00\x01' # Minor version.
190 '\x00\x00\x05\xb9' # Descriptor size.
Jan Monsch1f58eec2020-02-10 17:05:15 +0100191 '\x00\x02' # Number of ICP entries.
192 + self.test_entry_1_bytes
193 + self.test_entry_2_bytes)
194
Jan Monsch0ec6b9a2020-01-29 14:38:46 +0100195 # pylint: disable=no-member
196 self.test_afi_resp = proto.api_pb2.AddFirmwareInfoResponse()
197 self.test_afi_resp.fw_info_proof.proof.leaf_index = 6263
198 hashes = [
199 '3ad99869646980c0a51d637a9791f892d12e0bc83f6bac5d305a9e289e7f7e8b',
200 '2e5c664d2aee64f71cb4d292e787d0eae7ca9ed80d1e08abb41d26baca386c05',
201 'a671dd99f8d97e9155cc2f0a9dc776a112a5ec5b821ec71571bb258ac790717a',
202 '78046b839595e4e49ad4b0c73f92bf4803aacd4a3351181086509d057ef0d7a9',
203 'c0a7e013f03e7c69e9402070e113dadb345868cf144ccb174fabc384b5605abf',
204 'dc36e5dbe36abe9f4ad10f14170aa0148b6fe3fcaba9df43deaf4dede01b02e8',
205 'b063e7fb665370a361718208756c363dc5206e2e9af9b4d847d81289cdae30de',
206 'a69ea5ba88a221103636d3f4245c800570eb86ad9276121481521f97d0a04a81']
207 for h in hashes:
208 self.test_afi_resp.fw_info_proof.proof.hashes.append(
209 binascii.unhexlify(h))
210 self.test_afi_resp.fw_info_proof.sth.key_hint = binascii.unhexlify(
211 '5af859abce8fe1ea')
212 self.test_afi_resp.fw_info_proof.sth.log_root = binascii.unhexlify(
213 '000100000000000018782053b182b55dc1377197c938637f50093131daea4'
214 'd0696b1eae5b8a014bfde884a15edb28f1fc7954400000000000013a50000'
215 )
Jan Monsch27905072020-02-11 15:40:32 +0100216 self.test_afi_resp.fw_info_proof.sth.log_root_signature = binascii.unhexlify(
Jan Monsch0ec6b9a2020-01-29 14:38:46 +0100217 'c264bc7986a1cf56364ca4dd04989f45515cb8764d05b4fb2b880172585ea404'
218 '2105f95a0e0471fb6e0f8c762b14b2e526fb78eaddcc61484917795a12f6ab3b'
219 '557b5571d492d07d7950595f9ad8647a606c7c633f4697c5eb59c272aeca0419'
220 '397c70a3b9b51537537c4ea6b49d356110e70a9286902f814cc6afbeafe612e4'
221 '9e180146140e902bdd9e9dae66b37b4943150a9571949027a648db88a4eea3ad'
222 'f930b4fa6a183e97b762ab0e55a3a26aa6b0fd44d30531e2541ecb94bf645e62'
223 '59e8e3151e7c3b51a09fe24557ce2fd2c0ecdada7ce99c390d2ef10e5d075801'
224 '7c10d49c55cdee930959cc35f0104e04f296591eeb5defbc9ebb237da7b204ca'
225 'a4608cb98d6bc3a01f18585a04441caf8ec7a35aa2d35f7483b92b14fd0f4a41'
226 '3a91133545579309adc593222ca5032a103b00d8fcaea911936dbec11349e4dd'
227 '419b091ea7d1130570d70e2589dd9445fd77fd7492507e1c87736847b9741cc6'
228 '236868af42558ff6e833e12010c8ede786e43ada40ff488f5f1870d1619887d7'
229 '66a24ad0a06a47cc14e2f7db07361be191172adf3155f49713807c7c265f5a84'
230 '040fc84246ccf7913e44721f0043cea05ee774e457e13206775eee992620c3f9'
231 'd2b2584f58aac19e4afe35f0a17df699c45729f94101083f9fc4302659a7e6e0'
232 'e7eb36f8d1ca0be2c9010160d329bd2d17bb707b010fdd63c30b667a0b886cf9'
233 )
234 self.test_afi_resp.fw_info_leaf = (
235 '{\"timestamp\":{\"seconds\":1580115370,\"nanos\":621454825},\"Va'
236 'lue\":{\"FwInfo\":{\"info\":{\"info\":{\"vbmeta_hash\":\"ViNzEQS'
237 '/oc/bJ13yl40fk/cvXw90bxHQbzCRxgHDIGc=\",\"version_incremental\":'
238 '\"1\",\"manufacturer_key_hash\":\"yBCrUOdjvaAh4git5EgqWa5neegUao'
239 'XeLlB67+N8ObY=\"}}}}}')
240
Jan Monsch6990f5d2020-02-12 18:25:51 +0100241 self.test_fw_info_leaf = aftltool.FirmwareInfoLeaf(
242 self.test_afi_resp.fw_info_leaf)
243
Dan Austin715f5032020-01-08 10:18:05 -0800244 def tearDown(self):
245 """Tears down the test bed for the unit tests."""
246 # Reconnects stderr back to the normal stderr; see setUp() for details.
247 sys.stderr = self.stderr
248
Jan Monsch27905072020-02-11 15:40:32 +0100249 super(AftltoolTestCase, self).tearDown()
Dan Austin715f5032020-01-08 10:18:05 -0800250
Jan Monsch443bf322020-02-19 14:56:44 +0100251 def get_testdata_path(self, relative_path):
252 """Retrieves the absolute path for testdata given the relative path.
253
254 Arguments:
255 relative_path: The relative path to the testdata in the testdata
256 directory.
257
258 Returns:
259 The absolute path to the testdata.
260 """
261 rel_path_parts = ['test', 'data']
262 rel_path_parts.extend(relative_path.split(os.path.sep))
263 return os.path.join(TEST_EXEC_PATH, *rel_path_parts)
264
Dan Austin715f5032020-01-08 10:18:05 -0800265
266class AftltoolTest(AftltoolTestCase):
267
Dan Austin715f5032020-01-08 10:18:05 -0800268 def test_merkle_root_hash(self):
269 """Tests validation of inclusion proof and the merkle tree calculations.
270
271 The test vectors have been taken from the Trillian tests:
272 https://github.com/google/trillian/blob/v1.3.3/merkle/log_verifier_test.go
273 """
274
275 inclusion_proofs = [
276 (1,
277 8,
278 [
279 binascii.unhexlify('96a296d224f285c67bee93c30f8a3091'
280 '57f0daa35dc5b87e410b78630a09cfc7'),
281 binascii.unhexlify('5f083f0a1a33ca076a95279832580db3'
282 'e0ef4584bdff1f54c8a360f50de3031e'),
283 binascii.unhexlify('6b47aaf29ee3c2af9af889bc1fb9254d'
284 'abd31177f16232dd6aab035ca39bf6e4')
285 ]),
286 (6,
287 8,
288 [
289 binascii.unhexlify('bc1a0643b12e4d2d7c77918f44e0f4f7'
290 '9a838b6cf9ec5b5c283e1f4d88599e6b'),
291 binascii.unhexlify('ca854ea128ed050b41b35ffc1b87b8eb'
292 '2bde461e9e3b5596ece6b9d5975a0ae0'),
293 binascii.unhexlify('d37ee418976dd95753c1c73862b9398f'
294 'a2a2cf9b4ff0fdfe8b30cd95209614b7')
295 ]),
296 (3,
297 3,
298 [
299 binascii.unhexlify('fac54203e7cc696cf0dfcb42c92a1d9d'
300 'baf70ad9e621f4bd8d98662f00e3c125')
301 ]),
302 (2,
303 5,
304 [
305 binascii.unhexlify('6e340b9cffb37a989ca544e6bb780a2c'
306 '78901d3fb33738768511a30617afa01d'),
307 binascii.unhexlify('5f083f0a1a33ca076a95279832580db3'
308 'e0ef4584bdff1f54c8a360f50de3031e'),
309 binascii.unhexlify('bc1a0643b12e4d2d7c77918f44e0f4f7'
310 '9a838b6cf9ec5b5c283e1f4d88599e6b')
311 ]
312 )
313 ]
314
315 leaves = [
316 binascii.unhexlify(''),
317 binascii.unhexlify('00'),
318 binascii.unhexlify('10'),
319 binascii.unhexlify('2021'),
320 binascii.unhexlify('3031'),
321 binascii.unhexlify('40414243'),
322 binascii.unhexlify('5051525354555657'),
323 binascii.unhexlify('606162636465666768696a6b6c6d6e6f'),
324 ]
325
326 roots = [
327 binascii.unhexlify('6e340b9cffb37a989ca544e6bb780a2c'
328 '78901d3fb33738768511a30617afa01d'),
329 binascii.unhexlify('fac54203e7cc696cf0dfcb42c92a1d9d'
330 'baf70ad9e621f4bd8d98662f00e3c125'),
331 binascii.unhexlify('aeb6bcfe274b70a14fb067a5e5578264'
332 'db0fa9b51af5e0ba159158f329e06e77'),
333 binascii.unhexlify('d37ee418976dd95753c1c73862b9398f'
334 'a2a2cf9b4ff0fdfe8b30cd95209614b7'),
335 binascii.unhexlify('4e3bbb1f7b478dcfe71fb631631519a3'
336 'bca12c9aefca1612bfce4c13a86264d4'),
337 binascii.unhexlify('76e67dadbcdf1e10e1b74ddc608abd2f'
338 '98dfb16fbce75277b5232a127f2087ef'),
339 binascii.unhexlify('ddb89be403809e325750d3d263cd7892'
340 '9c2942b7942a34b77e122c9594a74c8c'),
341 binascii.unhexlify('5dc9da79a70659a9ad559cb701ded9a2'
342 'ab9d823aad2f4960cfe370eff4604328'),
343 ]
344
345 for icp in inclusion_proofs:
346 leaf_id = icp[0] - 1
347 leaf_hash = aftltool.rfc6962_hash_leaf(leaves[leaf_id])
348 root_hash = aftltool.root_from_icp(leaf_id, icp[1], icp[2], leaf_hash)
349 self.assertEqual(root_hash, roots[icp[1] -1])
350
351
Jan Monsch1f58eec2020-02-10 17:05:15 +0100352class AftlDescriptorTest(AftltoolTestCase):
353
354 def test__init__(self):
355 """Tests the constructor."""
356 # Calls constructor without data.
357 d = aftltool.AftlDescriptor()
Jan Monsch27905072020-02-11 15:40:32 +0100358 self.assertIsInstance(d.icp_header, aftltool.AftlIcpHeader)
Jan Monsch1f58eec2020-02-10 17:05:15 +0100359 self.assertEqual(d.icp_header.icp_count, 0)
360 self.assertEqual(d.icp_entries, [])
361 self.assertTrue(d.is_valid())
362
363 # Calls constructor with data.
364 d = aftltool.AftlDescriptor(self.test_expected_aftl_descriptor_bytes)
Jan Monsch27905072020-02-11 15:40:32 +0100365 self.assertIsInstance(d.icp_header, aftltool.AftlIcpHeader)
Jan Monsch1f58eec2020-02-10 17:05:15 +0100366 self.assertEqual(d.icp_header.icp_count, 2)
367 self.assertEqual(len(d.icp_entries), 2)
368 for entry in d.icp_entries:
Jan Monsch27905072020-02-11 15:40:32 +0100369 self.assertIsInstance(entry, aftltool.AftlIcpEntry)
Jan Monsch1f58eec2020-02-10 17:05:15 +0100370 self.assertTrue(d.is_valid())
371
372 def test_add_icp_entry(self):
373 """Tests the add_icp_entry method."""
374 d = aftltool.AftlDescriptor()
375
376 # Adds 1st ICP.
377 d.add_icp_entry(self.test_entry_1)
378 self.assertEqual(d.icp_header.icp_count, 1)
379 self.assertEqual(len(d.icp_entries), 1)
380 self.assertTrue(d.is_valid())
381
382 # Adds 2nd ICP.
383 d.add_icp_entry(self.test_entry_2)
384 self.assertEqual(d.icp_header.icp_count, 2)
385 self.assertEqual(len(d.icp_entries), 2)
386 self.assertTrue(d.is_valid())
387
Jan Monsch682c27e2020-02-20 14:12:09 +0100388 def test_verify_vbmeta_image_with_1_icp(self):
Jan Monsch443bf322020-02-19 14:56:44 +0100389 """Tests the verify_vbmeta_image method."""
Jan Monsch682c27e2020-02-20 14:12:09 +0100390 # Valid vbmeta image without footer with 1 ICP.
Jan Monsch443bf322020-02-19 14:56:44 +0100391 tool = aftltool.Aftl()
Jan Monsch682c27e2020-02-20 14:12:09 +0100392 image_path = self.get_testdata_path(
393 'aftltool/aftl_output_vbmeta_with_1_icp.img')
394 vbmeta_image, _ = tool.get_vbmeta_image(image_path)
395 desc = tool.get_aftl_descriptor(image_path)
396
397 # Valid image checked against correct log key.
Jan Monsch443bf322020-02-19 14:56:44 +0100398 self.assertTrue(desc.verify_vbmeta_image(
399 vbmeta_image, [self.get_testdata_path('aftltool/aftl_pubkey_1.pub')]))
400
Jan Monsch682c27e2020-02-20 14:12:09 +0100401 # Valid image checked with a key from another log.
Jan Monsch443bf322020-02-19 14:56:44 +0100402 self.assertFalse(desc.verify_vbmeta_image(
403 vbmeta_image, [self.get_testdata_path('aftltool/aftl_pubkey_2.pub')]))
404
Jan Monsch682c27e2020-02-20 14:12:09 +0100405 # Valid image checked with non existed key file path.
406 self.assertFalse(desc.verify_vbmeta_image(
407 vbmeta_image, [self.get_testdata_path('non_existent_blabli')]))
408
409 # Valid image checked with an invalid key.
Jan Monsch443bf322020-02-19 14:56:44 +0100410 self.assertFalse(desc.verify_vbmeta_image(
411 vbmeta_image, [self.get_testdata_path('large_blob.bin')]))
412
Jan Monsch682c27e2020-02-20 14:12:09 +0100413 # Valid image checked with empty list of keys.
414 self.assertFalse(desc.verify_vbmeta_image(vbmeta_image, []))
415
416 # Valid image checked with empty list of keys.
417 self.assertFalse(desc.verify_vbmeta_image(vbmeta_image, None))
418
419 def test_verify_vbmeta_image_with_2_icp_from_same_log(self):
420 """Tests the verify_vbmeta_image method."""
421 # Valid vbmeta image without footer with 2 ICPs from same log.
422 tool = aftltool.Aftl()
423 image_path = self.get_testdata_path(
424 'aftltool/aftl_output_vbmeta_with_2_icp_same_log.img')
425 vbmeta_image, _ = tool.get_vbmeta_image(image_path)
426 desc = tool.get_aftl_descriptor(image_path)
427
428 # Valid image checked against correct log key.
429 self.assertTrue(desc.verify_vbmeta_image(
430 vbmeta_image, [self.get_testdata_path('aftltool/aftl_pubkey_1.pub')]))
431
432 # Valid vbmeta image checked with key from another log.
433 self.assertFalse(desc.verify_vbmeta_image(
434 vbmeta_image, [self.get_testdata_path('aftltool/aftl_pubkey_2.pub')]))
435
436 # Valid image checked with non existed key file path.
437 self.assertFalse(desc.verify_vbmeta_image(
438 vbmeta_image, [self.get_testdata_path('non_existent_blabli')]))
439
440 # Valid image checked with invalid key.
441 self.assertFalse(desc.verify_vbmeta_image(
442 vbmeta_image, [self.get_testdata_path('large_blob.bin')]))
443
444 # Valid image but checked with empty list of keys.
445 self.assertFalse(desc.verify_vbmeta_image(vbmeta_image, []))
446
447 def test_verify_vbmeta_image_with_2_icp_from_different_logs(self):
448 """Tests the verify_vbmeta_image method."""
449 # Valid vbmeta image without footer with 2 ICPs from different logs.
450 tool = aftltool.Aftl()
451 image_path = self.get_testdata_path(
452 'aftltool/aftl_output_vbmeta_with_2_icp_different_logs.img')
453 vbmeta_image, _ = tool.get_vbmeta_image(image_path)
454 desc = tool.get_aftl_descriptor(image_path)
455
456 # Valid image checked against log keys from both logs.
457 self.assertTrue(desc.verify_vbmeta_image(
458 vbmeta_image, [
459 self.get_testdata_path('aftltool/aftl_pubkey_1.pub'),
460 self.get_testdata_path('aftltool/aftl_pubkey_2.pub')
461 ]))
462
463 # Valid image checked with one of the keys with an invalid file path.
464 self.assertFalse(desc.verify_vbmeta_image(
465 vbmeta_image, [
466 self.get_testdata_path('aftltool/aftl_pubkey_1.pub'),
467 self.get_testdata_path('non_existent_blabli')
468 ]))
469
470 # Valid image checked with one of the keys being a invalid key.
471 self.assertFalse(desc.verify_vbmeta_image(
472 vbmeta_image, [
473 self.get_testdata_path('aftltool/aftl_pubkey_1.pub'),
474 self.get_testdata_path('large_blob.bin')
475 ]))
476
477 # Valid image checked with one of the keys being None.
478 self.assertFalse(desc.verify_vbmeta_image(
479 vbmeta_image, [
480 self.get_testdata_path('aftltool/aftl_pubkey_1.pub'),
481 None
482 ]))
483
484 # Valid vbmeta image checked against only one of the log keys.
485 self.assertFalse(desc.verify_vbmeta_image(
486 vbmeta_image, [self.get_testdata_path('aftltool/aftl_pubkey_1.pub')]))
487 self.assertFalse(desc.verify_vbmeta_image(
488 vbmeta_image, [self.get_testdata_path('aftltool/aftl_pubkey_2.pub')]))
489
490 # Valid image checked with invalid key.
491 self.assertFalse(desc.verify_vbmeta_image(
492 vbmeta_image, [self.get_testdata_path('large_blob.bin')]))
493
494 # Valid image but checked with empty list of keys.
495 self.assertFalse(desc.verify_vbmeta_image(vbmeta_image, []))
496
Jan Monsch1f58eec2020-02-10 17:05:15 +0100497 def test_save(self):
498 """Tests save method."""
499 buf = io.BytesIO()
500 self.test_aftl_desc.save(buf)
501 self.assertEqual(buf.getvalue(), self.test_expected_aftl_descriptor_bytes)
502
503 def test_encode(self):
504 """Tests encode method."""
505 desc_bytes = self.test_aftl_desc.encode()
506 self.assertEqual(desc_bytes, self.test_expected_aftl_descriptor_bytes)
507
508 def test_is_valid(self):
509 """Tests is_valid method."""
510 d = aftltool.AftlDescriptor()
511 d.add_icp_entry(self.test_entry_1)
512 d.add_icp_entry(self.test_entry_2)
513
Jan Monsch27905072020-02-11 15:40:32 +0100514 # Force invalid ICP header.
Jan Monsch1f58eec2020-02-10 17:05:15 +0100515 old_magic = d.icp_header.magic
516 d.icp_header.magic = 'YOLO'
517 self.assertFalse(d.is_valid())
518 d.icp_header.magic = old_magic
519 self.assertTrue(d.is_valid())
520
521 # Force count mismatch between header and actual entries.
522 old_icp_count = d.icp_header.icp_count
523 d.icp_header.icp_count = 1
524 self.assertFalse(d.is_valid())
525 d.icp_header.icp_count = old_icp_count
526 self.assertTrue(d.is_valid())
527
Jan Monsch27905072020-02-11 15:40:32 +0100528 # Force invalid ICP entry.
529 old_leaf_index = d.icp_entries[0].leaf_index
530 d.icp_entries[0].leaf_index = -10
Jan Monsch1f58eec2020-02-10 17:05:15 +0100531 self.assertFalse(d.is_valid())
Jan Monsch27905072020-02-11 15:40:32 +0100532 d.icp_entries[0].leaf_index = old_leaf_index
Jan Monsch1f58eec2020-02-10 17:05:15 +0100533 self.assertTrue(d.is_valid())
534
535 def test_print_desc(self):
536 """Tests print_desc method."""
537 buf = io.BytesIO()
538 self.test_aftl_desc.print_desc(buf)
539 desc = buf.getvalue()
540
541 # Cursory check whether the printed description contains something useful.
542 self.assertGreater(len(desc), 0)
Jan Monsch27905072020-02-11 15:40:32 +0100543 self.assertIn('Log Root Descriptor:', desc)
Jan Monsch1f58eec2020-02-10 17:05:15 +0100544
545
Jan Monsch58be6b32020-01-29 17:13:02 +0100546class AftlIcpHeaderTest(AftltoolTestCase):
547 """Test suite for testing the AftlIcpHeader descriptor."""
548
549 def setUp(self):
550 """Sets up the test bed for the unit tests."""
551 super(AftlIcpHeaderTest, self).setUp()
552
553 self.test_header_valid = aftltool.AftlIcpHeader()
554 self.test_header_valid.icp_count = 1
555
556 self.test_header_invalid = aftltool.AftlIcpHeader()
557 self.test_header_invalid.icp_count = -34
558
Jan Monsch1f58eec2020-02-10 17:05:15 +0100559 self.test_header_bytes = bytearray('\x41\x46\x54\x4c\x00\x00\x00\x01'
Jan Monsch58be6b32020-01-29 17:13:02 +0100560 '\x00\x00\x00\x01\x00\x00\x00\x12'
561 '\x00\x01')
562
563 def test__init__(self):
Jan Monsch27905072020-02-11 15:40:32 +0100564 """Tests constructor."""
Jan Monsch58be6b32020-01-29 17:13:02 +0100565
566 # Calls constructor without data.
567 header = aftltool.AftlIcpHeader()
568 self.assertEqual(header.magic, 'AFTL')
569 self.assertEqual(header.required_icp_version_major,
570 avbtool.AVB_VERSION_MAJOR)
571 self.assertEqual(header.required_icp_version_minor,
572 avbtool.AVB_VERSION_MINOR)
573 self.assertEqual(header.aftl_descriptor_size, aftltool.AftlIcpHeader.SIZE)
574 self.assertEqual(header.icp_count, 0)
575 self.assertTrue(header.is_valid())
576
577 # Calls constructor with data.
578 header = aftltool.AftlIcpHeader(self.test_header_bytes)
579 self.assertEqual(header.magic, 'AFTL')
580 self.assertEqual(header.required_icp_version_major, 1)
581 self.assertEqual(header.required_icp_version_minor, 1)
582 self.assertEqual(header.aftl_descriptor_size, aftltool.AftlIcpHeader.SIZE)
583 self.assertTrue(header.icp_count, 1)
584 self.assertTrue(header.is_valid())
585
586 def test_save(self):
Jan Monsch27905072020-02-11 15:40:32 +0100587 """Tests save method."""
Jan Monsch58be6b32020-01-29 17:13:02 +0100588 buf = io.BytesIO()
589 self.test_header_valid.save(buf)
590 self.assertEqual(buf.getvalue(), self.test_header_bytes)
591
592 def test_encode(self):
Jan Monsch27905072020-02-11 15:40:32 +0100593 """Tests encode method."""
Jan Monsch58be6b32020-01-29 17:13:02 +0100594 # Valid header.
595 header_bytes = self.test_header_valid.encode()
596 self.assertEqual(header_bytes, self.test_header_bytes)
597
598 # Invalid header
599 with self.assertRaises(aftltool.AftlError):
600 header_bytes = self.test_header_invalid.encode()
601
602 def test_is_valid(self):
Jan Monsch27905072020-02-11 15:40:32 +0100603 """Tests is_valid method."""
604 # Valid default record.
Jan Monsch58be6b32020-01-29 17:13:02 +0100605 header = aftltool.AftlIcpHeader()
606 self.assertTrue(header.is_valid())
607
608 # Invalid magic.
609 header = aftltool.AftlIcpHeader()
610 header.magic = 'YOLO'
611 self.assertFalse(header.is_valid())
612
613 # Valid ICP count.
614 self.assertTrue(self.test_header_valid.is_valid())
615
616 # Invalid ICP count.
617 self.assertFalse(self.test_header_invalid.is_valid())
618
619 header = aftltool.AftlIcpHeader()
620 header.icp_count = 10000000
621 self.assertFalse(header.is_valid())
622
623 # Invalid ICP major version.
624 header = aftltool.AftlIcpHeader()
625 header.required_icp_version_major = avbtool.AVB_VERSION_MAJOR + 1
626 self.assertFalse(header.is_valid())
627
628 # Invalid ICP minor version.
629 header = aftltool.AftlIcpHeader()
630 header.required_icp_version_minor = avbtool.AVB_VERSION_MINOR + 1
631 self.assertFalse(header.is_valid())
632
633 def test_print_desc(self):
Jan Monschd4515eb2020-01-29 16:57:50 +0100634 """Tests print_desc method."""
Jan Monsch58be6b32020-01-29 17:13:02 +0100635 buf = io.BytesIO()
636 self.test_header_valid.print_desc(buf)
637 desc = buf.getvalue()
638
Jan Monsch1f58eec2020-02-10 17:05:15 +0100639 # Cursory check whether the printed description contains something useful.
Jan Monsch58be6b32020-01-29 17:13:02 +0100640 self.assertGreater(len(desc), 0)
Jan Monsch27905072020-02-11 15:40:32 +0100641 self.assertIn('Major version:', desc)
642
643
644class AftlIcpEntryTest(AftltoolTestCase):
645 """Test suite for testing the AftlIcpEntry descriptor."""
646
647 def test__init__and_properties(self):
648 """Tests constructor and properties methods."""
649
650 # Calls constructor without data.
651 entry = aftltool.AftlIcpEntry()
652 self.assertEqual(entry.log_url_size, 0)
653 self.assertEqual(entry.leaf_index, 0)
654 self.assertEqual(entry.log_root_descriptor_size, 29)
655 self.assertEqual(entry.fw_info_leaf_size, 0)
656 self.assertEqual(entry.log_root_sig_size, 0)
657 self.assertEqual(entry.proof_hash_count, 0)
658 self.assertEqual(entry.inc_proof_size, 0)
659 self.assertEqual(entry.log_url, '')
660 self.assertIsInstance(entry.log_root_descriptor,
661 aftltool.TrillianLogRootDescriptor)
662 self.assertEqual(entry.proofs, [])
663 self.assertTrue(entry.is_valid())
664
665 # Calls constructor with data.
666 entry = aftltool.AftlIcpEntry(self.test_entry_1_bytes)
667 self.assertEqual(entry.log_url_size, len(self.test_tl_url_1))
668 self.assertEqual(entry.leaf_index, 1)
669 self.assertEqual(entry.fw_info_leaf_size, 0)
670 self.assertEqual(entry.log_root_sig_size, 512)
671 self.assertEqual(entry.proof_hash_count, len(self.test_proof_hashes_1))
672 self.assertEqual(entry.inc_proof_size, 128)
673 self.assertEqual(entry.log_url, self.test_tl_url_1)
674 self.assertEqual(entry.proofs, self.test_proof_hashes_1)
675 self.assertTrue(entry.is_valid())
676
677 def test_encode(self):
678 """Tests encode method."""
679 entry_bytes = self.test_entry_1.encode()
680 self.assertEqual(entry_bytes, self.test_entry_1_bytes)
681
682 def test_save(self):
683 """Tests save method."""
684 buf = io.BytesIO()
685 self.test_entry_1.save(buf)
686 self.assertEqual(buf.getvalue(), self.test_entry_1_bytes)
687
688 def test_get_expected_size(self):
689 """Tests get_expected_size method."""
690 # Default record.
691 entry = aftltool.AftlIcpEntry()
692 self.assertEqual(entry.get_expected_size(), 56)
693 self.assertEqual(entry.get_expected_size(), len(entry.encode()))
694
695 # Test record.
696 self.assertEqual(self.test_entry_1.get_expected_size(), 755)
697 self.assertEqual(self.test_entry_1.get_expected_size(),
698 len(self.test_entry_1.encode()))
699
700 def test_is_valid(self):
701 """Tests is_valid method."""
702 # Valid default record.
703 entry = aftltool.AftlIcpEntry()
704 entry.leaf_index = 2
705 entry.log_url = self.test_tl_url_1
706 entry.set_log_root_descriptor = self.test_sth_1
707 entry.proofs = self.test_proof_hashes_1
708 self.assertTrue(entry.is_valid())
709
710 # Invalid leaf index.
711 entry = aftltool.AftlIcpEntry()
712 entry.leaf_index = -1
713 self.assertFalse(entry.is_valid())
714
715 # Invalid log_root_descriptor
716 entry = aftltool.AftlIcpEntry()
717 entry.log_root_descriptor = None
718 self.assertFalse(entry.is_valid())
719
720 entry.log_root_descriptor = ''
721 self.assertFalse(entry.is_valid())
722
723 entry.log_root_descriptor = 'blabli'
724 self.assertFalse(entry.is_valid())
725
726 def test_translate_response(self):
727 """Tests translate_response method."""
728 entry = aftltool.AftlIcpEntry()
729 entry.translate_response('aftl-test.foo.bar:80', self.test_afi_resp)
730 self.assertEqual(entry.log_url, 'aftl-test.foo.bar:80')
731 self.assertEqual(entry.leaf_index, 6263)
732 self.assertEqual(entry.log_root_descriptor.encode(),
733 self.test_afi_resp.fw_info_proof.sth.log_root)
734 self.assertEqual(entry.log_root_signature,
735 self.test_afi_resp.fw_info_proof.sth.log_root_signature)
736 self.assertEqual(entry.proofs,
737 self.test_afi_resp.fw_info_proof.proof.hashes)
738
Jan Monsch27905072020-02-11 15:40:32 +0100739 def test_verify_icp(self):
740 """Tests verify_icp method."""
Jan Monsch90c7e582020-02-13 16:56:56 +0100741 key_file = 'transparency_log_pub_key.pem'
742 with open(key_file, 'w') as f:
743 f.write(self.test_aftl_pub_key)
744
745 # Valid ICP.
746 entry = aftltool.AftlIcpEntry()
747 entry.translate_response(self.test_tl_url_1, self.test_afi_resp)
748 self.assertTrue(entry.verify_icp(key_file))
749
750 # Invalid ICP where fw_info_leaf is not matching up with proofs.
751 entry = aftltool.AftlIcpEntry()
752 entry.translate_response(self.test_tl_url_1, self.test_afi_resp)
753 fw_info_leaf_bytes = entry.fw_info_leaf._fw_info_leaf_bytes.replace(
754 'ViNzEQS', '1234567')
755 entry.fw_info_leaf._fw_info_leaf_bytes = fw_info_leaf_bytes
756 self.assertFalse(entry.verify_icp(key_file))
757
758 os.remove(key_file)
Jan Monsch27905072020-02-11 15:40:32 +0100759
Jan Monsch443bf322020-02-19 14:56:44 +0100760 def test_verify_vbmeta_image(self):
761 """Tests the verify_vbmeta_image method."""
Jan Monsch682c27e2020-02-20 14:12:09 +0100762 # Valid vbmeta image without footer with 1 ICP.
Jan Monsch443bf322020-02-19 14:56:44 +0100763 tool = aftltool.Aftl()
Jan Monsch682c27e2020-02-20 14:12:09 +0100764 image_path = self.get_testdata_path(
765 'aftltool/aftl_output_vbmeta_with_1_icp.img')
766 vbmeta_image, _ = tool.get_vbmeta_image(image_path)
767 desc = tool.get_aftl_descriptor(image_path)
Jan Monsch443bf322020-02-19 14:56:44 +0100768
Jan Monsch682c27e2020-02-20 14:12:09 +0100769 # Checks that there is 1 ICP.
Jan Monsch443bf322020-02-19 14:56:44 +0100770 self.assertEqual(desc.icp_header.icp_count, 1)
771 entry = desc.icp_entries[0]
Jan Monsch682c27e2020-02-20 14:12:09 +0100772
773 # Valid vbmeta image checked with correct log key.
Jan Monsch443bf322020-02-19 14:56:44 +0100774 self.assertTrue(entry.verify_vbmeta_image(
775 vbmeta_image, self.get_testdata_path('aftltool/aftl_pubkey_1.pub')))
776
Jan Monsch682c27e2020-02-20 14:12:09 +0100777 # Valid vbmeta image checked with public key of another log.
Jan Monsch443bf322020-02-19 14:56:44 +0100778 self.assertFalse(entry.verify_vbmeta_image(
779 vbmeta_image, self.get_testdata_path('aftltool/aftl_pubkey_2.pub')))
780
Jan Monsch682c27e2020-02-20 14:12:09 +0100781 # Valid vbmeta image checked with invalid key.
Jan Monsch443bf322020-02-19 14:56:44 +0100782 self.assertFalse(entry.verify_vbmeta_image(
783 vbmeta_image, self.get_testdata_path('large_blob.bin')))
784
Jan Monsch682c27e2020-02-20 14:12:09 +0100785 # Valid vbmeta image checked with no key.
786 self.assertFalse(entry.verify_vbmeta_image(vbmeta_image, None))
787
788 def test_verify_invalid_vbmeta_image(self):
789 """Tests the verify_vbmeta_image method."""
790 # Valid vbmeta image without footer with 1 ICP.
791 tool = aftltool.Aftl()
792 image_path = self.get_testdata_path(
793 'aftltool/aftl_output_vbmeta_with_1_icp.img')
794 vbmeta_image, _ = tool.get_vbmeta_image(image_path)
795 desc = tool.get_aftl_descriptor(image_path)
796
797 self.assertEqual(desc.icp_header.icp_count, 1)
798 entry = desc.icp_entries[0]
799
800 # Modify vbmeta image to become invalid
801 vbmeta_image = 'A' * len(vbmeta_image)
802
803 # Invalid vbmeta image checked with correct log key.
804 self.assertFalse(entry.verify_vbmeta_image(
805 vbmeta_image, self.get_testdata_path('aftltool/aftl_pubkey_1.pub')))
806
807 # Invalid vbmeta image checked with invalid key.
808 self.assertFalse(entry.verify_vbmeta_image(
809 vbmeta_image, self.get_testdata_path('large_blob.bin')))
810
811 # Valid vbmeta image checked with no key.
812 self.assertFalse(entry.verify_vbmeta_image(vbmeta_image, None))
813
814 # None image checked with a key.
815 self.assertFalse(entry.verify_vbmeta_image(
816 None, self.get_testdata_path('aftltool/aftl_pubkey_1.pub')))
817
Jan Monsch27905072020-02-11 15:40:32 +0100818 def test_print_desc(self):
819 """Tests print_desc method."""
820 buf = io.BytesIO()
821 self.test_entry_1.print_desc(buf)
822 desc = buf.getvalue()
823
824 # Cursory check whether the printed description contains something useful.
825 self.assertGreater(len(desc), 0)
826 self.assertIn('ICP hashes:', desc)
Jan Monsch58be6b32020-01-29 17:13:02 +0100827
828
Dan Austin715f5032020-01-08 10:18:05 -0800829class TrillianLogRootDescriptorTest(AftltoolTestCase):
Jan Monsch6990f5d2020-02-12 18:25:51 +0100830 """Test suite for testing the TrillianLogRootDescriptor descriptor."""
Dan Austin715f5032020-01-08 10:18:05 -0800831
832 def setUp(self):
833 """Sets up the test bed for the unit tests."""
834 super(TrillianLogRootDescriptorTest, self).setUp()
Jan Monschd4515eb2020-01-29 16:57:50 +0100835
836 # Creates basic log root without metadata fields.
Dan Austin715f5032020-01-08 10:18:05 -0800837 base_log_root = (
838 '0001' # version
839 '00000000000002e5' # tree_size
840 '20' # root_hash_size
841 '2d614759ad408a111a3351c0cb33c099' # root_hash
842 '422c30a5c5104788a343332bde2b387b'
843 '15e1c97e3b4bd239' # timestamp
844 '00000000000002e4' # revision
845 )
Jan Monschd4515eb2020-01-29 16:57:50 +0100846
847 # Create valid log roots with metadata fields w/ and w/o metadata.
848 self.test_log_root_bytes_wo_metadata = binascii.unhexlify(
Dan Austin715f5032020-01-08 10:18:05 -0800849 base_log_root + '0000')
Jan Monschd4515eb2020-01-29 16:57:50 +0100850 self.test_log_root_bytes_with_metadata = binascii.unhexlify(
Dan Austin715f5032020-01-08 10:18:05 -0800851 base_log_root + '00023132')
852
Jan Monschd4515eb2020-01-29 16:57:50 +0100853 def test__init__(self):
854 """Tests constructor."""
855 # Calls constructor without data.
Dan Austin715f5032020-01-08 10:18:05 -0800856 d = aftltool.TrillianLogRootDescriptor()
857 self.assertTrue(d.is_valid())
Jan Monschd4515eb2020-01-29 16:57:50 +0100858 self.assertEqual(d.version, 1)
859 self.assertEqual(d.tree_size, 0)
860 self.assertEqual(d.root_hash_size, 0)
861 self.assertEqual(d.root_hash, bytearray())
862 self.assertEqual(d.timestamp, 0)
863 self.assertEqual(d.revision, 0)
864 self.assertEqual(d.metadata_size, 0)
865 self.assertEqual(d.metadata, bytearray())
Dan Austin715f5032020-01-08 10:18:05 -0800866
Jan Monschd4515eb2020-01-29 16:57:50 +0100867 # Calls constructor with log_root w/o metadata
868 d = aftltool.TrillianLogRootDescriptor(self.test_log_root_bytes_wo_metadata)
Dan Austin715f5032020-01-08 10:18:05 -0800869 self.assertTrue(d.is_valid())
870 self.assertEqual(d.version, 1)
871 self.assertEqual(d.tree_size, 741)
872 self.assertEqual(d.root_hash_size, 32)
Jan Monschd4515eb2020-01-29 16:57:50 +0100873 self.assertEqual(d.root_hash,
874 binascii.unhexlify('2d614759ad408a111a3351c0cb33c099'
875 '422c30a5c5104788a343332bde2b387b'))
Dan Austin715f5032020-01-08 10:18:05 -0800876 self.assertEqual(d.timestamp, 1576762888554271289)
877 self.assertEqual(d.revision, 740)
878 self.assertEqual(d.metadata_size, 0)
879 self.assertEqual(d.metadata, bytearray())
880
Jan Monschd4515eb2020-01-29 16:57:50 +0100881 # Calls constructor with log_root with metadata
882 d = aftltool.TrillianLogRootDescriptor(
883 self.test_log_root_bytes_with_metadata)
Dan Austin715f5032020-01-08 10:18:05 -0800884 self.assertEqual(d.metadata_size, 2)
885 self.assertEqual(d.metadata, bytearray('12'))
886
Jan Monschd4515eb2020-01-29 16:57:50 +0100887 def test_get_expected_size(self):
888 """Tests get_expected_size method."""
889 # Default constructor.
890 d = aftltool.TrillianLogRootDescriptor()
891 self.assertEqual(d.get_expected_size(), 11 + 18)
892
893 # Log root without metadata.
894 d = aftltool.TrillianLogRootDescriptor(self.test_log_root_bytes_wo_metadata)
895 self.assertEqual(d.get_expected_size(), 11 + 18 + 32)
896
897 # Log root with metadata.
898 d = aftltool.TrillianLogRootDescriptor(
899 self.test_log_root_bytes_with_metadata)
900 self.assertEqual(d.get_expected_size(), 11 + 18 + 32 + 2)
901
902 def test_encode(self):
903 """Tests encode method."""
904 # Log root from default constructor.
905 d = aftltool.TrillianLogRootDescriptor()
906 expected_bytes = (
907 '0001' # version
908 '0000000000000000' # tree_size
909 '00' # root_hash_size
910 '' # root_hash (empty)
911 '0000000000000000' # timestamp
912 '0000000000000000' # revision
913 '0000' # metadata size
914 '' # metadata (empty)
915 )
916 self.assertEqual(d.encode(), binascii.unhexlify(expected_bytes))
917
918 # Log root without metadata.
919 d = aftltool.TrillianLogRootDescriptor(self.test_log_root_bytes_wo_metadata)
920 self.assertEqual(d.encode(), self.test_log_root_bytes_wo_metadata)
921
922 # Log root with metadata.
923 d = aftltool.TrillianLogRootDescriptor(
924 self.test_log_root_bytes_with_metadata)
925 self.assertEqual(d.encode(), self.test_log_root_bytes_with_metadata)
926
927 def test_is_valid(self):
Jan Monsch27905072020-02-11 15:40:32 +0100928 """Tests is_valid method."""
Jan Monschd4515eb2020-01-29 16:57:50 +0100929 d = aftltool.TrillianLogRootDescriptor()
930 self.assertTrue(d.is_valid())
931
932 # Invalid version.
933 d = aftltool.TrillianLogRootDescriptor()
934 d.version = 2
935 self.assertFalse(d.is_valid())
936
937 # Invalid tree_size.
938 d = aftltool.TrillianLogRootDescriptor()
939 d.tree_size = -1
940 self.assertFalse(d.is_valid())
941
942 # Invalid root_hash_size.
943 d = aftltool.TrillianLogRootDescriptor()
944 d.root_hash_size = -1
945 self.assertFalse(d.is_valid())
946 d.root_hash_size = 300
947 self.assertFalse(d.is_valid())
948
949 # Invalid/valid root_hash_size / root_hash combination.
950 d = aftltool.TrillianLogRootDescriptor()
951 d.root_hash_size = 4
952 d.root_hash = '123'
953 self.assertFalse(d.is_valid())
954 d.root_hash = '1234'
955 self.assertTrue(d.is_valid())
956
957 # Invalid timestamp.
958 d = aftltool.TrillianLogRootDescriptor()
959 d.timestamp = -1
960 self.assertFalse(d.is_valid())
961
962 # Invalid revision.
963 d = aftltool.TrillianLogRootDescriptor()
964 d.revision = -1
965 self.assertFalse(d.is_valid())
966
967 # Invalid metadata_size.
968 d = aftltool.TrillianLogRootDescriptor()
969 d.metadata_size = -1
970 self.assertFalse(d.is_valid())
971 d.metadata_size = 70000
972 self.assertFalse(d.is_valid())
973
974 # Invalid/valid metadata_size / metadata combination.
975 d = aftltool.TrillianLogRootDescriptor()
976 d.metadata_size = 4
977 d.metadata = '123'
978 self.assertFalse(d.is_valid())
979 d.metadata = '1234'
980 self.assertTrue(d.is_valid())
981
982 def test_print_desc(self):
983 """Tests print_desc method."""
984 # Log root without metadata
985 buf = io.BytesIO()
986 d = aftltool.TrillianLogRootDescriptor(self.test_log_root_bytes_wo_metadata)
987 d.print_desc(buf)
988 desc = buf.getvalue()
989
Jan Monsch1f58eec2020-02-10 17:05:15 +0100990 # Cursory check whether the printed description contains something useful.
Jan Monschd4515eb2020-01-29 16:57:50 +0100991 self.assertGreater(len(desc), 0)
Jan Monsch27905072020-02-11 15:40:32 +0100992 self.assertIn('Version:', desc)
993 self.assertNotIn('Metadata:', desc)
Jan Monschd4515eb2020-01-29 16:57:50 +0100994
995 # Log root with metadata
996 buf = io.BytesIO()
997 d = aftltool.TrillianLogRootDescriptor(
998 self.test_log_root_bytes_with_metadata)
999 d.print_desc(buf)
1000 desc = buf.getvalue()
1001
Jan Monsch1f58eec2020-02-10 17:05:15 +01001002 # Cursory check whether the printed description contains something useful.
Jan Monschd4515eb2020-01-29 16:57:50 +01001003 self.assertGreater(len(desc), 0)
Jan Monsch27905072020-02-11 15:40:32 +01001004 self.assertIn('Version:', desc)
1005 self.assertIn('Metadata:', desc)
Jan Monschd4515eb2020-01-29 16:57:50 +01001006
Dan Austin715f5032020-01-08 10:18:05 -08001007
Jan Monsch6990f5d2020-02-12 18:25:51 +01001008class FirmwareInfoLeafTest(AftltoolTestCase):
1009 """Test suite for testing the FirmwareInfoLeaf."""
1010
1011 def test__init__(self):
1012 """Tests constructor and properties methods."""
1013 # Calls constructor without data.
1014 leaf = aftltool.FirmwareInfoLeaf()
1015 self.assertTrue(leaf.is_valid())
1016 self.assertEqual(leaf.vbmeta_hash, None)
1017 self.assertEqual(leaf.version_incremental, None)
1018 self.assertEqual(leaf.platform_key, None)
1019 self.assertEqual(leaf.manufacturer_key_hash, None)
1020 self.assertEqual(leaf.description, None)
1021
1022 # Calls constructor with data.
1023 leaf = aftltool.FirmwareInfoLeaf(self.test_afi_resp.fw_info_leaf)
1024 self.assertTrue(leaf.is_valid())
1025 self.assertEqual(
1026 leaf.vbmeta_hash,
1027 base64.b64decode('ViNzEQS/oc/bJ13yl40fk/cvXw90bxHQbzCRxgHDIGc='))
1028 self.assertEqual(leaf.version_incremental, '1')
1029 self.assertEqual(leaf.platform_key, None)
1030 self.assertEqual(
1031 leaf.manufacturer_key_hash,
1032 base64.b64decode('yBCrUOdjvaAh4git5EgqWa5neegUaoXeLlB67+N8ObY='))
1033 self.assertEqual(leaf.description, None)
1034
1035 # Calls constructor with invalid JSON data.
1036 with self.assertRaises(aftltool.AftlError):
1037 leaf = aftltool.FirmwareInfoLeaf('Invalid JSON.')
1038
1039 def test_get_expected_size(self):
1040 """Tests get_expected_size method."""
1041 # Calls constructor without data.
1042 leaf = aftltool.FirmwareInfoLeaf()
1043 self.assertEqual(leaf.get_expected_size(), 0)
1044
1045 # Calls constructor with data.
1046 leaf = aftltool.FirmwareInfoLeaf(self.test_afi_resp.fw_info_leaf)
1047 self.assertEqual(leaf.get_expected_size(),
1048 len(self.test_afi_resp.fw_info_leaf))
1049
1050 def test_encode(self):
1051 """Tests encode method."""
1052 # Calls constructor without data.
1053 leaf = aftltool.FirmwareInfoLeaf()
1054 self.assertEqual(leaf.encode(), '')
1055
1056 # Calls constructor with data.
1057 self.assertEqual(self.test_fw_info_leaf.encode(),
1058 self.test_afi_resp.fw_info_leaf)
1059
1060 def test_is_valid(self):
1061 """Tests is_valid method."""
1062 # Calls constructor without data.
1063 leaf = aftltool.FirmwareInfoLeaf()
1064 self.assertTrue(leaf.is_valid())
1065
1066 # Calls constructor with data.
1067 self.assertTrue(self.test_fw_info_leaf.is_valid())
1068
1069 # Incorrect name for Value key.
1070 invalid_value_key_name = (
1071 '{\"timestamp\":{\"seconds\":1580115370,\"nanos\":621454825},\"In'
1072 'val\":{\"FwInfo\":{\"info\":{\"info\":{\"vbmeta_hash\":\"ViNzEQS'
1073 '/oc/bJ13yl40fk/cvXw90bxHQbzCRxgHDIGc=\",\"version_incremental\":'
1074 '\"1\",\"manufacturer_key_hash\":\"yBCrUOdjvaAh4git5EgqWa5neegUao'
1075 'XeLlB67+N8ObY=\"}}}}}')
1076
1077 with self.assertRaises(aftltool.AftlError):
1078 aftltool.FirmwareInfoLeaf(invalid_value_key_name)
1079
1080 # Within Firmware Info having a field which does not exist in
1081 # proto.aftl_pb2.FirmwareInfo.
1082 invalid_fields = (
1083 '{\"timestamp\":{\"seconds\":1580115370,\"nanos\":621454825},\"Va'
1084 'lue\":{\"FwInfo\":{\"info\":{\"info\":{\"invalid_field\":\"ViNzEQS'
1085 '/oc/bJ13yl40fk/cvXw90bxHQbzCRxgHDIGc=\",\"version_incremental\":'
1086 '\"1\",\"manufacturer_key_hash\":\"yBCrUOdjvaAh4git5EgqWa5neegUao'
1087 'XeLlB67+N8ObY=\"}}}}}')
1088
1089 with self.assertRaises(aftltool.AftlError):
1090 aftltool.FirmwareInfoLeaf(invalid_fields)
1091
1092 def test_print_desc(self):
1093 """Tests print_desc method."""
1094 buf = io.BytesIO()
1095 self.test_fw_info_leaf.print_desc(buf)
1096 desc = buf.getvalue()
1097
1098 # Cursory check whether the printed description contains something useful.
1099 self.assertGreater(len(desc), 0)
1100 self.assertIn('VBMeta hash:', desc)
1101
1102
Jan Monsch0ec6b9a2020-01-29 14:38:46 +01001103class AftlMockCommunication(aftltool.AftlCommunication):
1104 """Testing Mock implementation of AftlCommunication."""
1105
1106 def __init__(self, transparency_log, canned_response):
1107 """Initializes the object.
1108
1109 Arguments:
1110 transparency_log: String containing the URL of a transparency log server.
1111 canned_response: AddFirmwareInfoResponse to return or the Exception to
1112 raise.
1113 """
Jan Monsch6439f992020-02-17 16:11:29 +01001114 super(AftlMockCommunication, self).__init__(transparency_log, timeout=None)
Jan Monsch0ec6b9a2020-01-29 14:38:46 +01001115 self.request = None
1116 self.canned_response = canned_response
1117
Jan Monsch27905072020-02-11 15:40:32 +01001118 def add_firmware_info(self, request):
Jan Monsch0ec6b9a2020-01-29 14:38:46 +01001119 """Records the request and returns the canned response."""
1120 self.request = request
1121
1122 if isinstance(self.canned_response, aftltool.AftlError):
1123 raise self.canned_response
1124 return self.canned_response
1125
1126
Jan Monsch57149852020-02-19 18:22:16 +01001127class AftlMock(aftltool.Aftl):
1128 """Mock for aftltool.Aftl to mock the communication piece."""
1129
1130 def __init__(self, canned_response):
1131 """Initializes the object.
1132
1133 Arguments:
1134 canned_response: AddFirmwareInfoResponse to return or the Exception to
1135 raise.
1136 """
1137 self.mock_canned_response = canned_response
1138
1139 def request_inclusion_proof(self, transparency_log, vbmeta_descriptor,
1140 version_inc, manufacturer_key_path,
1141 signing_helper, signing_helper_with_files,
1142 timeout, aftl_comms=None):
1143 """Mocked request_inclusion_proof function."""
1144 aftl_comms = AftlMockCommunication(transparency_log,
1145 self.mock_canned_response)
1146 return super(AftlMock, self).request_inclusion_proof(
1147 transparency_log, vbmeta_descriptor, version_inc, manufacturer_key_path,
1148 signing_helper, signing_helper_with_files, timeout,
1149 aftl_comms=aftl_comms)
1150
1151
1152class AftlTestCase(AftltoolTestCase):
1153
1154 def setUp(self):
1155 """Sets up the test bed for the unit tests."""
1156 super(AftlTestCase, self).setUp()
1157 self.set_up_environment()
1158 self.output_filename = 'vbmeta_icp.img'
1159
1160 self.make_icp_default_params = {
1161 'vbmeta_image_path': self.vbmeta_image,
1162 'output': None,
1163 'signing_helper': None,
1164 'signing_helper_with_files': None,
1165 'version_incremental': '1',
1166 'transparency_log_servers': [self.aftl_host],
1167 'transparency_log_pub_keys': [self.aftl_pubkey],
1168 'manufacturer_key': self.manufacturer_key,
1169 'padding_size': 0,
1170 'timeout': None
1171 }
1172
1173 self.info_icp_default_params = {
1174 'vbmeta_image_path': self.output_filename,
1175 'output': io.BytesIO()
1176 }
1177
1178 self.verify_icp_default_params = {
1179 'vbmeta_image_path': self.output_filename,
1180 'transparency_log_pub_keys': [self.aftl_pubkey],
1181 'output': io.BytesIO()
1182 }
1183
1184 self.load_test_aftl_default_params = {
1185 'vbmeta_image_path': self.vbmeta_image,
1186 'output': io.BytesIO(),
1187 'transparency_log_server': self.aftl_host,
1188 'transparency_log_pub_key': self.aftl_pubkey,
1189 'manufacturer_key': self.manufacturer_key,
1190 'process_count': 1,
1191 'submission_count': 1,
1192 'stats_filename': None,
1193 'preserve_icp_images': False,
1194 'timeout': None
1195 }
1196
1197 self.load_test_stats_file_p1_s1 = 'load_test_p1_s1.csv'
1198 self.load_test_stats_file_p2_p2 = 'load_test_p2_s2.csv'
1199
1200 self.files_to_cleanup = [
1201 self.output_filename,
1202 self.load_test_stats_file_p1_s1,
1203 self.load_test_stats_file_p2_p2
1204 ]
1205
1206 def tearDown(self):
1207 """Tears down the test bed for the unit tests."""
1208 for filename in self.files_to_cleanup:
1209 try:
1210 os.remove(filename)
1211 except OSError:
1212 pass
1213 super(AftlTestCase, self).tearDown()
1214
1215 def set_up_environment(self):
1216 """Sets up member variables for the particular test environment.
1217
1218 This allows to have different settings and mocking for unit tests and
1219 integration tests.
1220 """
1221 raise NotImplementedError('set_up_environment() needs to be implemented '
1222 'by subclass.')
1223
1224 def get_aftl_implementation(self):
1225 """Gets the aftltool.Aftl implementation used for testing.
1226
1227 This allows to have different Aftl implementations for unit tests and
1228 integration tests.
1229 """
1230 raise NotImplementedError('get_aftl_implementation() needs to be'
1231 'implemented by subclass.')
1232
1233
1234class AftlTest(AftlTestCase):
Jan Monsch0ec6b9a2020-01-29 14:38:46 +01001235
1236 def setUp(self):
1237 """Sets up the test bed for the unit tests."""
1238 super(AftlTest, self).setUp()
1239 self.mock_aftl_host = 'test.foo.bar:9000'
1240
Jan Monsch57149852020-02-19 18:22:16 +01001241 def set_up_environment(self):
1242 """Sets up the environment for unit testing without networking."""
1243 self.aftl_host = 'test.foo.bar:9000'
1244 self.aftl_pubkey = self.get_testdata_path('aftltool/aftl_pubkey_1.pub')
1245 self.vbmeta_image = self.get_testdata_path('aftltool/aftl_input_vbmeta.img')
1246 self.manufacturer_key = self.get_testdata_path('testkey_rsa4096.pem')
1247
1248 def get_aftl_implementation(self, canned_response):
1249 """Retrieves the AftlMock for unit testing without networking."""
1250 return AftlMock(canned_response)
1251
Jan Monsch443bf322020-02-19 14:56:44 +01001252 def test_get_vbmeta_image(self):
1253 """Tests the get_vbmeta_image method."""
1254 tool = aftltool.Aftl()
1255
1256 # Valid vbmeta image without footer and AftlDescriptor.
1257 image, footer = tool.get_vbmeta_image(
1258 self.get_testdata_path('aftltool/aftl_input_vbmeta.img'))
1259 self.assertIsNotNone(image)
1260 self.assertEqual(len(image), 4352)
1261 self.assertIsNone(footer)
1262
1263 # Valid vbmeta image without footer but with AftlDescriptor.
1264 image, footer = tool.get_vbmeta_image(
1265 self.get_testdata_path('aftltool/aftl_output_vbmeta_with_1_icp.img'))
1266 self.assertIsNotNone(image)
1267 self.assertEqual(len(image), 4352)
1268 self.assertIsNone(footer)
1269
1270 # Invalid vbmeta image.
1271 image, footer = tool.get_vbmeta_image(
1272 self.get_testdata_path('large_blob.bin'))
1273 self.assertIsNone(image)
1274 self.assertIsNone(footer)
1275
Jan Monsch59a63702020-02-20 15:42:51 +01001276 # Invalid file path.
1277 image, footer = tool.get_vbmeta_image(
1278 self.get_testdata_path('blabli_not_existing_file'))
1279 self.assertIsNone(image)
1280 self.assertIsNone(footer)
1281
Jan Monsch443bf322020-02-19 14:56:44 +01001282 def test_get_aftl_descriptor(self):
1283 """Tests the get_aftl_descriptor method."""
1284 tool = aftltool.Aftl()
1285
1286 # Valid vbmeta image without footer with AftlDescriptor.
1287 desc = tool.get_aftl_descriptor(
1288 self.get_testdata_path('aftltool/aftl_output_vbmeta_with_1_icp.img'))
1289 self.assertIsInstance(desc, aftltool.AftlDescriptor)
1290
1291 # Valid vbmeta image without footer and AftlDescriptor.
1292 desc = tool.get_aftl_descriptor(
1293 self.get_testdata_path('aftltool/aftl_input_vbmeta.img'))
1294 self.assertIsNone(desc)
1295
1296 # Invalid vbmeta image.
1297 desc = tool.get_aftl_descriptor(self.get_testdata_path('large_blob.bin'))
1298 self.assertIsNone(desc)
1299
Jan Monsch0ec6b9a2020-01-29 14:38:46 +01001300 # pylint: disable=no-member
1301 def test_request_inclusion_proof(self):
1302 """Tests the request_inclusion_proof method."""
Jan Monsch57149852020-02-19 18:22:16 +01001303 # Always work with a mock independent if run as unit or integration tests.
1304 aftl = AftlMock(self.test_afi_resp)
1305
Jan Monsch443bf322020-02-19 14:56:44 +01001306 icp = aftl.request_inclusion_proof(
1307 self.mock_aftl_host, 'a' * 1024, '1',
Jan Monsch57149852020-02-19 18:22:16 +01001308 self.get_testdata_path('testkey_rsa4096.pem'), None, None, None)
Jan Monsch0ec6b9a2020-01-29 14:38:46 +01001309 self.assertEqual(icp.leaf_index,
1310 self.test_afi_resp.fw_info_proof.proof.leaf_index)
1311 self.assertEqual(icp.proof_hash_count,
1312 len(self.test_afi_resp.fw_info_proof.proof.hashes))
1313 self.assertEqual(icp.log_url, self.mock_aftl_host)
1314 self.assertEqual(
1315 icp.log_root_descriptor.root_hash, binascii.unhexlify(
1316 '53b182b55dc1377197c938637f50093131daea4d0696b1eae5b8a014bfde884a'))
1317
Jan Monsch6990f5d2020-02-12 18:25:51 +01001318 self.assertEqual(icp.fw_info_leaf.version_incremental, '1')
Jan Monsch0ec6b9a2020-01-29 14:38:46 +01001319 # To calculate the hash of the a RSA key use the following command:
1320 # openssl rsa -in test/data/testkey_rsa4096.pem -pubout \
1321 # -outform DER | sha256sum
Jan Monsch6990f5d2020-02-12 18:25:51 +01001322 self.assertEqual(icp.fw_info_leaf.manufacturer_key_hash, base64.b64decode(
1323 'yBCrUOdjvaAh4git5EgqWa5neegUaoXeLlB67+N8ObY='))
Jan Monsch0ec6b9a2020-01-29 14:38:46 +01001324
1325 self.assertEqual(icp.log_root_signature,
1326 self.test_afi_resp.fw_info_proof.sth.log_root_signature)
1327 self.assertEqual(icp.proofs, self.test_afi_resp.fw_info_proof.proof.hashes)
1328
1329 # pylint: disable=no-member
1330 def test_request_inclusion_proof_failure(self):
Jan Monsch2da1c272020-02-19 20:54:37 +01001331 """Tests the request_inclusion_proof method in case of a comms problem."""
Jan Monsch57149852020-02-19 18:22:16 +01001332 # Always work with a mock independent if run as unit or integration tests.
1333 aftl = AftlMock(aftltool.AftlError('Comms error'))
1334
Jan Monsch0ec6b9a2020-01-29 14:38:46 +01001335 with self.assertRaises(aftltool.AftlError):
Jan Monsch443bf322020-02-19 14:56:44 +01001336 aftl.request_inclusion_proof(
1337 self.mock_aftl_host, 'a' * 1024, 'version_inc',
Jan Monsch57149852020-02-19 18:22:16 +01001338 self.get_testdata_path('testkey_rsa4096.pem'), None, None, None)
1339
Jan Monsch2da1c272020-02-19 20:54:37 +01001340 def test_request_inclusion_proof_manuf_key_not_4096(self):
1341 """Tests request_inclusion_proof with manufacturing key not of size 4096."""
1342 # Always work with a mock independent if run as unit or integration tests.
1343 aftl = AftlMock(self.test_afi_resp)
1344 with self.assertRaises(aftltool.AftlError) as e:
1345 aftl.request_inclusion_proof(
1346 self.mock_aftl_host, 'a' * 1024, 'version_inc',
1347 self.get_testdata_path('testkey_rsa2048.pem'), None, None, None)
1348 self.assertIn('not of size 4096: 2048', str(e.exception))
1349
Jan Monsch57149852020-02-19 18:22:16 +01001350 def test_make_and_verify_icp_with_1_log(self):
1351 """Tests make_icp_from_vbmeta, verify_image_icp & info_image_icp."""
1352 aftl = self.get_aftl_implementation(self.test_afi_resp)
1353
1354 # Make a VBmeta image with ICP.
1355 with open(self.output_filename, 'wb') as output_file:
1356 self.make_icp_default_params['output'] = output_file
1357 result = aftl.make_icp_from_vbmeta(**self.make_icp_default_params)
1358 self.assertTrue(result)
1359
1360 # Checks that there is 1 ICP.
1361 aftl_descriptor = aftl.get_aftl_descriptor(self.output_filename)
1362 self.assertEqual(aftl_descriptor.icp_header.icp_count, 1)
1363
1364 # Verifies the generated image.
1365 result = aftl.verify_image_icp(**self.verify_icp_default_params)
1366 self.assertTrue(result)
Jan Monsch682c27e2020-02-20 14:12:09 +01001367
Jan Monsch57149852020-02-19 18:22:16 +01001368 # Prints the image details.
1369 result = aftl.info_image_icp(**self.info_icp_default_params)
1370 self.assertTrue(result)
1371
1372 def test_make_and_verify_icp_with_2_logs(self):
1373 """Tests make_icp_from_vbmeta, verify_image_icp & info_image_icp."""
1374 aftl = self.get_aftl_implementation(self.test_afi_resp)
1375
1376 # Reconfigures default parameters with two transparency logs.
1377 self.make_icp_default_params['transparency_log_servers'] = [
1378 self.aftl_host, self.aftl_host]
1379 self.make_icp_default_params['transparency_log_pub_keys'] = [
1380 self.aftl_pubkey, self.aftl_pubkey]
1381
1382 # Make a VBmeta image with ICP.
1383 with open(self.output_filename, 'wb') as output_file:
1384 self.make_icp_default_params['output'] = output_file
1385 result = aftl.make_icp_from_vbmeta(
1386 **self.make_icp_default_params)
1387 self.assertTrue(result)
1388
1389 # Checks that there are 2 ICPs.
1390 aftl_descriptor = aftl.get_aftl_descriptor(self.output_filename)
1391 self.assertEqual(aftl_descriptor.icp_header.icp_count, 2)
1392
1393 # Verifies the generated image.
1394 result = aftl.verify_image_icp(**self.verify_icp_default_params)
1395 self.assertTrue(result)
1396
1397 # Prints the image details.
1398 result = aftl.info_image_icp(**self.info_icp_default_params)
1399 self.assertTrue(result)
1400
Jan Monsch59a63702020-02-20 15:42:51 +01001401 def test_info_image_icp(self):
1402 """Tests info_image_icp with vbmeta image with 2 ICP."""
1403 # Always work with a mock independent if run as unit or integration tests.
1404 aftl = AftlMock(self.test_afi_resp)
1405
1406 image_path = self.get_testdata_path(
1407 'aftltool/aftl_output_vbmeta_with_2_icp_different_logs.img')
1408 self.info_icp_default_params['vbmeta_image_path'] = image_path
1409
1410 # Verifies the generated image.
1411 result = aftl.info_image_icp(**self.info_icp_default_params)
1412 self.assertTrue(result)
1413
1414 def test_info_image_icp_fail(self):
1415 """Tests info_image_icp with invalid vbmeta image."""
1416 # Always work with a mock independent if run as unit or integration tests.
1417 aftl = AftlMock(self.test_afi_resp)
1418
1419 image_path = self.get_testdata_path('large_blob.bin')
1420 self.info_icp_default_params['vbmeta_image_path'] = image_path
1421
1422 # Verifies the generated image.
1423 result = aftl.info_image_icp(**self.info_icp_default_params)
1424 self.assertFalse(result)
1425
1426 def test_verify_image_icp(self):
1427 """Tets verify_image_icp with 2 ICP with all matching log keys."""
1428 # Always work with a mock independent if run as unit or integration tests.
1429 aftl = AftlMock(self.test_afi_resp)
1430
1431 image_path = self.get_testdata_path(
1432 'aftltool/aftl_output_vbmeta_with_2_icp_different_logs.img')
1433 self.verify_icp_default_params['vbmeta_image_path'] = image_path
1434 self.verify_icp_default_params['transparency_log_pub_keys'] = [
1435 self.get_testdata_path('aftltool/aftl_pubkey_1.pub'),
1436 self.get_testdata_path('aftltool/aftl_pubkey_2.pub')
1437 ]
1438
1439 result = aftl.verify_image_icp(**self.verify_icp_default_params)
1440 self.assertTrue(result)
1441
1442 def test_verify_image_icp_failure(self):
1443 """Tests verify_image_icp with 2 ICP but only one matching log key."""
1444 # Always work with a mock independent if run as unit or integration tests.
1445 aftl = AftlMock(self.test_afi_resp)
1446
1447 image_path = self.get_testdata_path(
1448 'aftltool/aftl_output_vbmeta_with_2_icp_different_logs.img')
1449 self.verify_icp_default_params['vbmeta_image_path'] = image_path
1450 self.verify_icp_default_params['transparency_log_pub_keys'] = [
1451 self.get_testdata_path('aftltool/aftl_pubkey_1.pub')
1452 ]
1453
1454 result = aftl.verify_image_icp(**self.verify_icp_default_params)
1455 self.assertFalse(result)
1456
Jan Monsch57149852020-02-19 18:22:16 +01001457 def test_make_icp_with_invalid_grpc_service(self):
1458 """Tests make_icp_from_vbmeta command with a host that does not support GRPC."""
1459 aftl = self.get_aftl_implementation(aftltool.AftlError('Comms error'))
1460 self.make_icp_default_params[
1461 'transparency_log_servers'] = ['www.google.com:80']
1462 with open(self.output_filename, 'wb') as output_file:
1463 self.make_icp_default_params['output'] = output_file
1464 result = aftl.make_icp_from_vbmeta(
1465 **self.make_icp_default_params)
1466 self.assertFalse(result)
1467
1468 def test_make_icp_grpc_timeout(self):
1469 """Tests make_icp_from_vbmeta command when running into GRPC timeout."""
1470 aftl = self.get_aftl_implementation(aftltool.AftlError('Comms error'))
1471
1472 # The timeout is set to 1 second which is way below the minimum processing
1473 # time of the transparency log per load test results in b/139407814#2 where
1474 # it was 3.43 seconds.
1475 self.make_icp_default_params['timeout'] = 1
1476 with open(self.output_filename, 'wb') as output_file:
1477 self.make_icp_default_params['output'] = output_file
1478 result = aftl.make_icp_from_vbmeta(
1479 **self.make_icp_default_params)
1480 self.assertFalse(result)
1481
1482 def test_load_test_single_process_single_submission(self):
1483 """Tests load_test_aftl command with 1 process which does 1 submission."""
1484 aftl = self.get_aftl_implementation(self.test_afi_resp)
1485
1486 result = aftl.load_test_aftl(**self.load_test_aftl_default_params)
1487 self.assertTrue(result)
1488
1489 output = self.load_test_aftl_default_params['output'].getvalue()
1490 self.assertRegexpMatches(output, 'Succeeded:.+?1\n')
1491 self.assertRegexpMatches(output, 'Failed:.+?0\n')
1492
1493 self.assertTrue(os.path.exists(self.load_test_stats_file_p1_s1))
1494
1495 def test_load_test_multi_process_multi_submission(self):
1496 """Tests load_test_aftl command with 2 processes and 2 submissions each."""
1497 aftl = self.get_aftl_implementation(self.test_afi_resp)
1498
1499 self.load_test_aftl_default_params['process_count'] = 2
1500 self.load_test_aftl_default_params['submission_count'] = 2
1501 result = aftl.load_test_aftl(**self.load_test_aftl_default_params)
1502 self.assertTrue(result)
1503
1504 output = self.load_test_aftl_default_params['output'].getvalue()
1505 self.assertRegexpMatches(output, 'Succeeded:.+?4\n')
1506 self.assertRegexpMatches(output, 'Failed:.+?0\n')
1507
1508 self.assertTrue(os.path.exists(self.load_test_stats_file_p2_p2))
1509
1510 def test_load_test_invalid_grpc_service(self):
1511 """Tests load_test_aftl command with a host that does not support GRPC."""
1512 aftl = self.get_aftl_implementation(aftltool.AftlError('Comms error'))
1513
1514 self.load_test_aftl_default_params[
1515 'transparency_log_server'] = 'www.google.com:80'
1516 result = aftl.load_test_aftl(**self.load_test_aftl_default_params)
1517 self.assertFalse(result)
1518
1519 output = self.load_test_aftl_default_params['output'].getvalue()
1520 self.assertRegexpMatches(output, 'Succeeded:.+?0\n')
1521 self.assertRegexpMatches(output, 'Failed:.+?1\n')
1522
1523 def test_load_test_grpc_timeout(self):
1524 """Tests load_test_aftl command when running into timeout."""
1525 aftl = self.get_aftl_implementation(aftltool.AftlError('Comms error'))
1526
1527 self.load_test_aftl_default_params['timeout'] = 1
1528 result = aftl.load_test_aftl(**self.load_test_aftl_default_params)
1529 self.assertFalse(result)
1530
1531 output = self.load_test_aftl_default_params['output'].getvalue()
1532 self.assertRegexpMatches(output, 'Succeeded:.+?0\n')
1533 self.assertRegexpMatches(output, 'Failed:.+?1\n')
1534
Jan Monsch0ec6b9a2020-01-29 14:38:46 +01001535
Dan Austin715f5032020-01-08 10:18:05 -08001536if __name__ == '__main__':
1537 unittest.main(verbosity=2)