1d583833aSAndrew Geissler# Copyright (C) 2022 Armin Kuster <akuster808@gmail.com> 2d583833aSAndrew Geissler# Copyright (C) 2022 Anton Antonov <Anton.Antonov@arm.com> 3d583833aSAndrew Geissler# 4d583833aSAndrew Geisslerimport re 5d583833aSAndrew Geisslerfrom tempfile import mkstemp 6d583833aSAndrew Geissler 7d583833aSAndrew Geisslerfrom oeqa.runtime.case import OERuntimeTestCase 8d583833aSAndrew Geisslerfrom oeqa.core.decorator.depends import OETestDepends 9d583833aSAndrew Geisslerfrom oeqa.runtime.decorator.package import OEHasPackage 10d583833aSAndrew Geisslerfrom oeqa.core.decorator.data import skipIfNotFeature 11d583833aSAndrew Geissler 12d583833aSAndrew Geisslerclass ParsecTest(OERuntimeTestCase): 13d583833aSAndrew Geissler @classmethod 14d583833aSAndrew Geissler def setUpClass(cls): 15d583833aSAndrew Geissler cls.toml_file = '/etc/parsec/config.toml' 1692b42cb3SPatrick Williams cls.tc.target.run('cp -p %s %s-original' % (cls.toml_file, cls.toml_file)) 17615f2f11SAndrew Geissler 18d583833aSAndrew Geissler def setUp(self): 19d583833aSAndrew Geissler super(ParsecTest, self).setUp() 20d583833aSAndrew Geissler if 'systemd' in self.tc.td['DISTRO_FEATURES']: 21d583833aSAndrew Geissler self.parsec_status='systemctl status -l parsec' 22d583833aSAndrew Geissler self.parsec_reload='systemctl restart parsec' 23d583833aSAndrew Geissler else: 24d583833aSAndrew Geissler self.parsec_status='pgrep -l parsec' 25d583833aSAndrew Geissler self.parsec_reload='/etc/init.d/parsec reload' 26d583833aSAndrew Geissler 27*8e7b46e2SPatrick Williams def tearDown(self): 28*8e7b46e2SPatrick Williams self.target.run('sync') 29*8e7b46e2SPatrick Williams super(ParsecTest, self).tearDown() 30*8e7b46e2SPatrick Williams 31d583833aSAndrew Geissler def copy_subconfig(self, cfg, provider): 32d583833aSAndrew Geissler """ Copy a provider configuration to target and append it to Parsec config """ 33d583833aSAndrew Geissler 34d583833aSAndrew Geissler tmp_fd, tmp_path = mkstemp() 35d583833aSAndrew Geissler with os.fdopen(tmp_fd, 'w') as f: 36d583833aSAndrew Geissler f.write('\n'.join(cfg)) 37d583833aSAndrew Geissler 38d583833aSAndrew Geissler (status, output) = self.target.copyTo(tmp_path, "%s-%s" % (self.toml_file, provider)) 39d583833aSAndrew Geissler self.assertEqual(status, 0, msg='File could not be copied.\n%s' % output) 40d583833aSAndrew Geissler status, output = self.target.run('cat %s-%s >>%s' % (self.toml_file, provider, self.toml_file)) 41d583833aSAndrew Geissler os.remove(tmp_path) 42d583833aSAndrew Geissler 4392b42cb3SPatrick Williams def restore_parsec_config(self): 4492b42cb3SPatrick Williams """ Restore original Parsec config """ 4592b42cb3SPatrick Williams self.target.run('cp -p %s-original %s' % (self.toml_file, self.toml_file)) 4692b42cb3SPatrick Williams self.target.run(self.parsec_reload) 4792b42cb3SPatrick Williams 48d583833aSAndrew Geissler def check_parsec_providers(self, provider=None, prov_id=None): 49d583833aSAndrew Geissler """ Get Parsec providers list and check for one if defined """ 50d583833aSAndrew Geissler 51d583833aSAndrew Geissler status, output = self.target.run(self.parsec_status) 52d583833aSAndrew Geissler self.assertEqual(status, 0, msg='Parsec service is not running.\n%s' % output) 53d583833aSAndrew Geissler 54d583833aSAndrew Geissler status, output = self.target.run('parsec-tool list-providers') 55d583833aSAndrew Geissler self.assertEqual(status, 0, msg='Cannot get a list of Parsec providers.\n%s' % output) 56d583833aSAndrew Geissler if provider and prov_id: 57d583833aSAndrew Geissler self.assertIn("ID: 0x0%d (%s provider)" % (prov_id, provider), 58d583833aSAndrew Geissler output, msg='%s provider is not configured.' % provider) 59d583833aSAndrew Geissler 60*8e7b46e2SPatrick Williams def run_cli_tests(self, prov_id=None, extra_params=""): 61d583833aSAndrew Geissler """ Run Parsec CLI end-to-end tests against one or all providers """ 62d583833aSAndrew Geissler 63*8e7b46e2SPatrick Williams status, output = self.target.run('parsec-cli-tests.sh %s %s' % ("-%d" % prov_id if prov_id else "", extra_params)) 64d583833aSAndrew Geissler self.assertEqual(status, 0, msg='Parsec CLI tests failed.\n %s' % output) 65d583833aSAndrew Geissler 6692b42cb3SPatrick Williams def check_packageconfig(self, prov): 6792b42cb3SPatrick Williams """ Check that the require provider is included in Parsec """ 68*8e7b46e2SPatrick Williams 69*8e7b46e2SPatrick Williams if 'PACKAGECONFIG:pn-parsec-service' in self.tc.td.keys(): 70*8e7b46e2SPatrick Williams providers = self.tc.td['PACKAGECONFIG:pn-parsec-service'] 71*8e7b46e2SPatrick Williams else: 72*8e7b46e2SPatrick Williams # PACKAGECONFIG is not defined in local.conf 73*8e7b46e2SPatrick Williams # Let's use the default value 74*8e7b46e2SPatrick Williams providers = "PKCS11 MBED-CRYPTO" 75*8e7b46e2SPatrick Williams if 'tpm2' in self.tc.td['DISTRO_FEATURES']: 76*8e7b46e2SPatrick Williams providers += " TPM" 77*8e7b46e2SPatrick Williams if prov not in providers: 7892b42cb3SPatrick Williams self.skipTest('%s provider is not included in Parsec. Parsec PACKAGECONFIG: "%s"' % \ 79*8e7b46e2SPatrick Williams (prov, providers)) 8092b42cb3SPatrick Williams 8192b42cb3SPatrick Williams def check_packages(self, prov, packages): 8292b42cb3SPatrick Williams """ Check for the required packages for Parsec providers software backends """ 8392b42cb3SPatrick Williams if isinstance(packages, str): 8492b42cb3SPatrick Williams need_pkgs = set([packages,]) 8592b42cb3SPatrick Williams else: 8692b42cb3SPatrick Williams need_pkgs = set(packages) 8792b42cb3SPatrick Williams 8892b42cb3SPatrick Williams if not self.tc.image_packages.issuperset(need_pkgs): 8992b42cb3SPatrick Williams self.skipTest('%s provider is not configured and packages "%s" are not included into the image' % \ 9092b42cb3SPatrick Williams (prov, need_pkgs)) 9192b42cb3SPatrick Williams 92d583833aSAndrew Geissler @OEHasPackage(['parsec-service']) 93d583833aSAndrew Geissler @OETestDepends(['ssh.SSHTest.test_ssh']) 94d583833aSAndrew Geissler def test_all_providers(self): 95d583833aSAndrew Geissler """ Test Parsec service with all pre-defined providers """ 96d583833aSAndrew Geissler 97d583833aSAndrew Geissler self.check_parsec_providers() 98d583833aSAndrew Geissler self.run_cli_tests() 99d583833aSAndrew Geissler 100d583833aSAndrew Geissler def configure_tpm_provider(self): 101d583833aSAndrew Geissler """ Create Parsec TPM provider configuration """ 102d583833aSAndrew Geissler 103d583833aSAndrew Geissler cfg = [ 104d583833aSAndrew Geissler '', 105d583833aSAndrew Geissler '[[provider]]', 106d583833aSAndrew Geissler 'name = "tpm-provider"', 107d583833aSAndrew Geissler 'provider_type = "Tpm"', 108d583833aSAndrew Geissler 'key_info_manager = "sqlite-manager"', 109d583833aSAndrew Geissler 'tcti = "swtpm:port=2321"', 110d583833aSAndrew Geissler 'owner_hierarchy_auth = ""', 111d583833aSAndrew Geissler ] 112d583833aSAndrew Geissler self.copy_subconfig(cfg, "TPM") 113d583833aSAndrew Geissler 114d583833aSAndrew Geissler cmds = [ 115d583833aSAndrew Geissler 'mkdir /tmp/myvtpm', 116d583833aSAndrew Geissler 'swtpm socket -d --tpmstate dir=/tmp/myvtpm --tpm2 --ctrl type=tcp,port=2322 --server type=tcp,port=2321 --flags not-need-init', 117d583833aSAndrew Geissler 'tpm2_startup -c -T "swtpm:port=2321"', 11892b42cb3SPatrick Williams 'chown -R parsec /tmp/myvtpm', 119d583833aSAndrew Geissler self.parsec_reload, 12092b42cb3SPatrick Williams 'sleep 5', 121d583833aSAndrew Geissler ] 122d583833aSAndrew Geissler 123d583833aSAndrew Geissler for cmd in cmds: 124d583833aSAndrew Geissler status, output = self.target.run(cmd) 125d583833aSAndrew Geissler self.assertEqual(status, 0, msg='\n'.join([cmd, output])) 126d583833aSAndrew Geissler 127d583833aSAndrew Geissler @OEHasPackage(['parsec-service']) 128d583833aSAndrew Geissler @skipIfNotFeature('tpm2','Test parsec_tpm_provider requires tpm2 to be in DISTRO_FEATURES') 12992b42cb3SPatrick Williams @OETestDepends(['ssh.SSHTest.test_ssh']) 130d583833aSAndrew Geissler def test_tpm_provider(self): 131d583833aSAndrew Geissler """ Configure and test Parsec TPM provider with swtpm as a backend """ 132d583833aSAndrew Geissler 13392b42cb3SPatrick Williams self.check_packageconfig("TPM") 13492b42cb3SPatrick Williams 13592b42cb3SPatrick Williams reconfigure = False 136d583833aSAndrew Geissler prov_id = 3 13792b42cb3SPatrick Williams try: 13892b42cb3SPatrick Williams # Chech if the provider is already configured 13992b42cb3SPatrick Williams self.check_parsec_providers("TPM", prov_id) 14092b42cb3SPatrick Williams except: 14192b42cb3SPatrick Williams # Try to test the provider with a software backend 14292b42cb3SPatrick Williams self.check_packages("TPM", ['swtpm', 'tpm2-tools']) 14392b42cb3SPatrick Williams reconfigure = True 144d583833aSAndrew Geissler self.configure_tpm_provider() 145d583833aSAndrew Geissler self.check_parsec_providers("TPM", prov_id) 14692b42cb3SPatrick Williams 147d583833aSAndrew Geissler self.run_cli_tests(prov_id) 14892b42cb3SPatrick Williams self.restore_parsec_config() 14992b42cb3SPatrick Williams 15092b42cb3SPatrick Williams if reconfigure: 15192b42cb3SPatrick Williams self.target.run('swtpm_ioctl -s --tcp :2322') 152d583833aSAndrew Geissler 153d583833aSAndrew Geissler def configure_pkcs11_provider(self): 154d583833aSAndrew Geissler """ Create Parsec PKCS11 provider configuration """ 155d583833aSAndrew Geissler 156d583833aSAndrew Geissler status, output = self.target.run('softhsm2-util --init-token --free --label "Parsec Service" --pin 123456 --so-pin 123456') 157d583833aSAndrew Geissler self.assertEqual(status, 0, msg='Failed to init PKCS11 token.\n%s' % output) 158d583833aSAndrew Geissler 159d583833aSAndrew Geissler slot = re.search('The token has been initialized and is reassigned to slot (\d*)', output) 160d583833aSAndrew Geissler if slot is None: 161d583833aSAndrew Geissler self.fail('Failed to get PKCS11 slot serial number.\n%s' % output) 162d583833aSAndrew Geissler self.assertNotEqual(slot.group(1), None, msg='Failed to get PKCS11 slot serial number.\n%s' % output) 163d583833aSAndrew Geissler 164d583833aSAndrew Geissler cfg = [ 165d583833aSAndrew Geissler '', 166d583833aSAndrew Geissler '[[provider]]', 167d583833aSAndrew Geissler 'name = "pkcs11-provider"', 168d583833aSAndrew Geissler 'provider_type = "Pkcs11"', 169d583833aSAndrew Geissler 'key_info_manager = "sqlite-manager"', 170d583833aSAndrew Geissler 'library_path = "/usr/lib/softhsm/libsofthsm2.so"', 171d583833aSAndrew Geissler 'slot_number = %s' % slot.group(1), 172d583833aSAndrew Geissler 'user_pin = "123456"', 173d583833aSAndrew Geissler 'allow_export = true', 174d583833aSAndrew Geissler ] 175d583833aSAndrew Geissler self.copy_subconfig(cfg, "PKCS11") 176d583833aSAndrew Geissler 177d583833aSAndrew Geissler status, output = self.target.run('for d in /var/lib/softhsm/tokens/*; do chown -R parsec $d; done') 178d583833aSAndrew Geissler status, output = self.target.run(self.parsec_reload) 179d583833aSAndrew Geissler self.assertEqual(status, 0, msg='Failed to reload Parsec.\n%s' % output) 180d583833aSAndrew Geissler 181d583833aSAndrew Geissler @OEHasPackage(['parsec-service']) 18292b42cb3SPatrick Williams @OETestDepends(['ssh.SSHTest.test_ssh']) 183d583833aSAndrew Geissler def test_pkcs11_provider(self): 184d583833aSAndrew Geissler """ Configure and test Parsec PKCS11 provider with softhsm as a backend """ 185d583833aSAndrew Geissler 18692b42cb3SPatrick Williams self.check_packageconfig("PKCS11") 187d583833aSAndrew Geissler prov_id = 2 18892b42cb3SPatrick Williams try: 18992b42cb3SPatrick Williams # Chech if the provider is already configured 19092b42cb3SPatrick Williams self.check_parsec_providers("PKCS #11", prov_id) 19192b42cb3SPatrick Williams except: 19292b42cb3SPatrick Williams # Try to test the provider with a software backend 19392b42cb3SPatrick Williams self.check_packages("PKCS11", 'softhsm') 194d583833aSAndrew Geissler self.configure_pkcs11_provider() 195d583833aSAndrew Geissler self.check_parsec_providers("PKCS #11", prov_id) 19692b42cb3SPatrick Williams 197*8e7b46e2SPatrick Williams # Software PKCS11 we use for OE QA testing 198*8e7b46e2SPatrick Williams # doesn't support RSA-OAEP(SHA256) encryption/decryption operations 199*8e7b46e2SPatrick Williams self.run_cli_tests(prov_id, "--no-oaep") 20092b42cb3SPatrick Williams self.restore_parsec_config() 20192b42cb3SPatrick Williams 20292b42cb3SPatrick Williams def configure_TS_provider(self): 20392b42cb3SPatrick Williams """ Create Trusted Services provider configuration """ 20492b42cb3SPatrick Williams 20592b42cb3SPatrick Williams cfg = [ 20692b42cb3SPatrick Williams '', 20792b42cb3SPatrick Williams '[[provider]]', 20892b42cb3SPatrick Williams 'name = "trusted-service-provider"', 20992b42cb3SPatrick Williams 'provider_type = "TrustedService"', 21092b42cb3SPatrick Williams 'key_info_manager = "sqlite-manager"', 21192b42cb3SPatrick Williams ] 21292b42cb3SPatrick Williams self.copy_subconfig(cfg, "TS") 21392b42cb3SPatrick Williams 21492b42cb3SPatrick Williams status, output = self.target.run(self.parsec_reload) 21592b42cb3SPatrick Williams self.assertEqual(status, 0, msg='Failed to reload Parsec.\n%s' % output) 21692b42cb3SPatrick Williams 21792b42cb3SPatrick Williams @OEHasPackage(['parsec-service']) 21892b42cb3SPatrick Williams @OETestDepends(['ssh.SSHTest.test_ssh']) 21992b42cb3SPatrick Williams def test_TS_provider(self): 22092b42cb3SPatrick Williams """ Configure and test Parsec PKCS11 provider with softhsm as a backend """ 22192b42cb3SPatrick Williams 22292b42cb3SPatrick Williams self.check_packageconfig("TS") 22392b42cb3SPatrick Williams prov_id = 4 22492b42cb3SPatrick Williams try: 22592b42cb3SPatrick Williams # Chech if the provider is already configured 22692b42cb3SPatrick Williams self.check_parsec_providers("Trusted Service", prov_id) 22792b42cb3SPatrick Williams except: 22892b42cb3SPatrick Williams self.configure_TS_provider() 22992b42cb3SPatrick Williams self.check_parsec_providers("Trusted Service", prov_id) 23092b42cb3SPatrick Williams 23192b42cb3SPatrick Williams self.run_cli_tests(prov_id) 23292b42cb3SPatrick Williams self.restore_parsec_config() 233