1# Copyright (C) 2022 Armin Kuster <akuster808@gmail.com> 2# Copyright (C) 2022 Anton Antonov <Anton.Antonov@arm.com> 3# 4import re 5from tempfile import mkstemp 6 7from oeqa.runtime.case import OERuntimeTestCase 8from oeqa.core.decorator.depends import OETestDepends 9from oeqa.runtime.decorator.package import OEHasPackage 10from oeqa.core.decorator.data import skipIfNotFeature 11 12class ParsecTest(OERuntimeTestCase): 13 @classmethod 14 def setUpClass(cls): 15 cls.toml_file = '/etc/parsec/config.toml' 16 cls.tc.target.run('cp -p %s %s-original' % (cls.toml_file, cls.toml_file)) 17 18 def setUp(self): 19 super(ParsecTest, self).setUp() 20 if 'systemd' in self.tc.td['DISTRO_FEATURES']: 21 self.parsec_status='systemctl status -l parsec' 22 self.parsec_reload='systemctl restart parsec' 23 else: 24 self.parsec_status='pgrep -l parsec' 25 self.parsec_reload='/etc/init.d/parsec reload' 26 27 def tearDown(self): 28 self.target.run('sync') 29 super(ParsecTest, self).tearDown() 30 31 def copy_subconfig(self, cfg, provider): 32 """ Copy a provider configuration to target and append it to Parsec config """ 33 34 tmp_fd, tmp_path = mkstemp() 35 with os.fdopen(tmp_fd, 'w') as f: 36 f.write('\n'.join(cfg)) 37 38 (status, output) = self.target.copyTo(tmp_path, "%s-%s" % (self.toml_file, provider)) 39 self.assertEqual(status, 0, msg='File could not be copied.\n%s' % output) 40 status, output = self.target.run('cat %s-%s >>%s' % (self.toml_file, provider, self.toml_file)) 41 os.remove(tmp_path) 42 43 def restore_parsec_config(self): 44 """ Restore original Parsec config """ 45 self.target.run('cp -p %s-original %s' % (self.toml_file, self.toml_file)) 46 self.target.run(self.parsec_reload) 47 48 def check_parsec_providers(self, provider=None, prov_id=None): 49 """ Get Parsec providers list and check for one if defined """ 50 51 status, output = self.target.run(self.parsec_status) 52 self.assertEqual(status, 0, msg='Parsec service is not running.\n%s' % output) 53 54 status, output = self.target.run('parsec-tool list-providers') 55 self.assertEqual(status, 0, msg='Cannot get a list of Parsec providers.\n%s' % output) 56 if provider and prov_id: 57 self.assertIn("ID: 0x0%d (%s provider)" % (prov_id, provider), 58 output, msg='%s provider is not configured.' % provider) 59 60 def run_cli_tests(self, prov_id=None, extra_params=""): 61 """ Run Parsec CLI end-to-end tests against one or all providers """ 62 63 status, output = self.target.run('parsec-cli-tests.sh %s %s' % ("-%d" % prov_id if prov_id else "", extra_params)) 64 self.assertEqual(status, 0, msg='Parsec CLI tests failed.\n %s' % output) 65 66 def check_packageconfig(self, prov): 67 """ Check that the require provider is included in Parsec """ 68 69 if 'PACKAGECONFIG:pn-parsec-service' in self.tc.td.keys(): 70 providers = self.tc.td['PACKAGECONFIG:pn-parsec-service'] 71 else: 72 # PACKAGECONFIG is not defined in local.conf 73 # Let's use the default value 74 providers = "PKCS11 MBED-CRYPTO" 75 if 'tpm2' in self.tc.td['DISTRO_FEATURES']: 76 providers += " TPM" 77 if prov not in providers: 78 self.skipTest('%s provider is not included in Parsec. Parsec PACKAGECONFIG: "%s"' % \ 79 (prov, providers)) 80 81 def check_packages(self, prov, packages): 82 """ Check for the required packages for Parsec providers software backends """ 83 if isinstance(packages, str): 84 need_pkgs = set([packages,]) 85 else: 86 need_pkgs = set(packages) 87 88 if not self.tc.image_packages.issuperset(need_pkgs): 89 self.skipTest('%s provider is not configured and packages "%s" are not included into the image' % \ 90 (prov, need_pkgs)) 91 92 @OEHasPackage(['parsec-service']) 93 @OETestDepends(['ssh.SSHTest.test_ssh']) 94 def test_all_providers(self): 95 """ Test Parsec service with all pre-defined providers """ 96 97 self.check_parsec_providers() 98 self.run_cli_tests() 99 100 def configure_tpm_provider(self): 101 """ Create Parsec TPM provider configuration """ 102 103 cfg = [ 104 '', 105 '[[provider]]', 106 'name = "tpm-provider"', 107 'provider_type = "Tpm"', 108 'key_info_manager = "sqlite-manager"', 109 'tcti = "swtpm:port=2321"', 110 'owner_hierarchy_auth = ""', 111 ] 112 self.copy_subconfig(cfg, "TPM") 113 114 cmds = [ 115 'mkdir /tmp/myvtpm', 116 'swtpm socket -d --tpmstate dir=/tmp/myvtpm --tpm2 --ctrl type=tcp,port=2322 --server type=tcp,port=2321 --flags not-need-init', 117 'tpm2_startup -c -T "swtpm:port=2321"', 118 'chown -R parsec /tmp/myvtpm', 119 self.parsec_reload, 120 'sleep 5', 121 ] 122 123 for cmd in cmds: 124 status, output = self.target.run(cmd) 125 self.assertEqual(status, 0, msg='\n'.join([cmd, output])) 126 127 @OEHasPackage(['parsec-service']) 128 @skipIfNotFeature('tpm2','Test parsec_tpm_provider requires tpm2 to be in DISTRO_FEATURES') 129 @OETestDepends(['ssh.SSHTest.test_ssh']) 130 def test_tpm_provider(self): 131 """ Configure and test Parsec TPM provider with swtpm as a backend """ 132 133 self.check_packageconfig("TPM") 134 135 reconfigure = False 136 prov_id = 3 137 try: 138 # Chech if the provider is already configured 139 self.check_parsec_providers("TPM", prov_id) 140 except: 141 # Try to test the provider with a software backend 142 self.check_packages("TPM", ['swtpm', 'tpm2-tools']) 143 reconfigure = True 144 self.configure_tpm_provider() 145 self.check_parsec_providers("TPM", prov_id) 146 147 self.run_cli_tests(prov_id) 148 self.restore_parsec_config() 149 150 if reconfigure: 151 self.target.run('swtpm_ioctl -s --tcp :2322') 152 153 def configure_pkcs11_provider(self): 154 """ Create Parsec PKCS11 provider configuration """ 155 156 status, output = self.target.run('softhsm2-util --init-token --free --label "Parsec Service" --pin 123456 --so-pin 123456') 157 self.assertEqual(status, 0, msg='Failed to init PKCS11 token.\n%s' % output) 158 159 slot = re.search('The token has been initialized and is reassigned to slot (\d*)', output) 160 if slot is None: 161 self.fail('Failed to get PKCS11 slot serial number.\n%s' % output) 162 self.assertNotEqual(slot.group(1), None, msg='Failed to get PKCS11 slot serial number.\n%s' % output) 163 164 cfg = [ 165 '', 166 '[[provider]]', 167 'name = "pkcs11-provider"', 168 'provider_type = "Pkcs11"', 169 'key_info_manager = "sqlite-manager"', 170 'library_path = "/usr/lib/softhsm/libsofthsm2.so"', 171 'slot_number = %s' % slot.group(1), 172 'user_pin = "123456"', 173 'allow_export = true', 174 ] 175 self.copy_subconfig(cfg, "PKCS11") 176 177 status, output = self.target.run('for d in /var/lib/softhsm/tokens/*; do chown -R parsec $d; done') 178 status, output = self.target.run(self.parsec_reload) 179 self.assertEqual(status, 0, msg='Failed to reload Parsec.\n%s' % output) 180 181 @OEHasPackage(['parsec-service']) 182 @OETestDepends(['ssh.SSHTest.test_ssh']) 183 def test_pkcs11_provider(self): 184 """ Configure and test Parsec PKCS11 provider with softhsm as a backend """ 185 186 self.check_packageconfig("PKCS11") 187 prov_id = 2 188 try: 189 # Chech if the provider is already configured 190 self.check_parsec_providers("PKCS #11", prov_id) 191 except: 192 # Try to test the provider with a software backend 193 self.check_packages("PKCS11", 'softhsm') 194 self.configure_pkcs11_provider() 195 self.check_parsec_providers("PKCS #11", prov_id) 196 197 # Software PKCS11 we use for OE QA testing 198 # doesn't support RSA-OAEP(SHA256) encryption/decryption operations 199 self.run_cli_tests(prov_id, "--no-oaep") 200 self.restore_parsec_config() 201 202 def configure_TS_provider(self): 203 """ Create Trusted Services provider configuration """ 204 205 cfg = [ 206 '', 207 '[[provider]]', 208 'name = "trusted-service-provider"', 209 'provider_type = "TrustedService"', 210 'key_info_manager = "sqlite-manager"', 211 ] 212 self.copy_subconfig(cfg, "TS") 213 214 status, output = self.target.run(self.parsec_reload) 215 self.assertEqual(status, 0, msg='Failed to reload Parsec.\n%s' % output) 216 217 @OEHasPackage(['parsec-service']) 218 @OETestDepends(['ssh.SSHTest.test_ssh']) 219 def test_TS_provider(self): 220 """ Configure and test Parsec PKCS11 provider with softhsm as a backend """ 221 222 self.check_packageconfig("TS") 223 prov_id = 4 224 try: 225 # Chech if the provider is already configured 226 self.check_parsec_providers("Trusted Service", prov_id) 227 except: 228 self.configure_TS_provider() 229 self.check_parsec_providers("Trusted Service", prov_id) 230 231 self.run_cli_tests(prov_id) 232 self.restore_parsec_config() 233