xref: /openbmc/openbmc/meta-security/meta-parsec/lib/oeqa/runtime/cases/parsec.py (revision 8e7b46e2350c3689938f309eaca929aada20b5a0)
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