signing.py 8.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215
  1. from oeqa.selftest.case import OESelftestTestCase
  2. from oeqa.utils.commands import runCmd, bitbake, get_bb_var, get_bb_vars
  3. import os
  4. import oe
  5. import glob
  6. import re
  7. import shutil
  8. import tempfile
  9. from contextlib import contextmanager
  10. from oeqa.core.decorator.oeid import OETestID
  11. from oeqa.utils.ftools import write_file
  12. class Signing(OESelftestTestCase):
  13. gpg_dir = ""
  14. pub_key_path = ""
  15. secret_key_path = ""
  16. def setup_gpg(self):
  17. bitbake('gnupg-native -c addto_recipe_sysroot')
  18. self.gpg_dir = tempfile.mkdtemp(prefix="oeqa-signing-")
  19. self.track_for_cleanup(self.gpg_dir)
  20. self.pub_key_path = os.path.join(self.testlayer_path, 'files', 'signing', "key.pub")
  21. self.secret_key_path = os.path.join(self.testlayer_path, 'files', 'signing', "key.secret")
  22. nsysroot = get_bb_var("RECIPE_SYSROOT_NATIVE", "gnupg-native")
  23. runCmd('gpg --batch --homedir %s --import %s %s' % (self.gpg_dir, self.pub_key_path, self.secret_key_path), native_sysroot=nsysroot)
  24. return nsysroot + get_bb_var("bindir_native")
  25. @contextmanager
  26. def create_new_builddir(self, builddir, newbuilddir):
  27. bb.utils.mkdirhier(newbuilddir)
  28. oe.path.copytree(builddir + "/conf", newbuilddir + "/conf")
  29. oe.path.copytree(builddir + "/cache", newbuilddir + "/cache")
  30. origenv = os.environ.copy()
  31. for e in os.environ:
  32. if builddir in os.environ[e]:
  33. os.environ[e] = os.environ[e].replace(builddir, newbuilddir)
  34. os.chdir(newbuilddir)
  35. try:
  36. yield
  37. finally:
  38. for e in origenv:
  39. os.environ[e] = origenv[e]
  40. os.chdir(builddir)
  41. @OETestID(1362)
  42. def test_signing_packages(self):
  43. """
  44. Summary: Test that packages can be signed in the package feed
  45. Expected: Package should be signed with the correct key
  46. Expected: Images can be created from signed packages
  47. Product: oe-core
  48. Author: Daniel Istrate <daniel.alexandrux.istrate@intel.com>
  49. Author: Alexander Kanavin <alex.kanavin@gmail.com>
  50. AutomatedBy: Daniel Istrate <daniel.alexandrux.istrate@intel.com>
  51. """
  52. import oe.packagedata
  53. self.setup_gpg()
  54. package_classes = get_bb_var('PACKAGE_CLASSES')
  55. if 'package_rpm' not in package_classes:
  56. self.skipTest('This test requires RPM Packaging.')
  57. test_recipe = 'ed'
  58. feature = 'INHERIT += "sign_rpm"\n'
  59. feature += 'RPM_GPG_PASSPHRASE = "test123"\n'
  60. feature += 'RPM_GPG_NAME = "testuser"\n'
  61. feature += 'GPG_PATH = "%s"\n' % self.gpg_dir
  62. self.write_config(feature)
  63. bitbake('-c clean %s' % test_recipe)
  64. bitbake('-f -c package_write_rpm %s' % test_recipe)
  65. self.add_command_to_tearDown('bitbake -c clean %s' % test_recipe)
  66. needed_vars = ['PKGDATA_DIR', 'DEPLOY_DIR_RPM', 'PACKAGE_ARCH', 'STAGING_BINDIR_NATIVE']
  67. bb_vars = get_bb_vars(needed_vars, test_recipe)
  68. pkgdatadir = bb_vars['PKGDATA_DIR']
  69. pkgdata = oe.packagedata.read_pkgdatafile(pkgdatadir + "/runtime/ed")
  70. if 'PKGE' in pkgdata:
  71. pf = pkgdata['PN'] + "-" + pkgdata['PKGE'] + pkgdata['PKGV'] + '-' + pkgdata['PKGR']
  72. else:
  73. pf = pkgdata['PN'] + "-" + pkgdata['PKGV'] + '-' + pkgdata['PKGR']
  74. deploy_dir_rpm = bb_vars['DEPLOY_DIR_RPM']
  75. package_arch = bb_vars['PACKAGE_ARCH'].replace('-', '_')
  76. staging_bindir_native = bb_vars['STAGING_BINDIR_NATIVE']
  77. pkg_deploy = os.path.join(deploy_dir_rpm, package_arch, '.'.join((pf, package_arch, 'rpm')))
  78. # Use a temporary rpmdb
  79. rpmdb = tempfile.mkdtemp(prefix='oeqa-rpmdb')
  80. runCmd('%s/rpmkeys --define "_dbpath %s" --import %s' %
  81. (staging_bindir_native, rpmdb, self.pub_key_path))
  82. ret = runCmd('%s/rpmkeys --define "_dbpath %s" --checksig %s' %
  83. (staging_bindir_native, rpmdb, pkg_deploy))
  84. # tmp/deploy/rpm/i586/ed-1.9-r0.i586.rpm: rsa sha1 md5 OK
  85. self.assertIn('digests signatures OK', ret.output, 'Package signed incorrectly.')
  86. shutil.rmtree(rpmdb)
  87. #Check that an image can be built from signed packages
  88. self.add_command_to_tearDown('bitbake -c clean core-image-minimal')
  89. bitbake('-c clean core-image-minimal')
  90. bitbake('core-image-minimal')
  91. @OETestID(1382)
  92. def test_signing_sstate_archive(self):
  93. """
  94. Summary: Test that sstate archives can be signed
  95. Expected: Package should be signed with the correct key
  96. Product: oe-core
  97. Author: Daniel Istrate <daniel.alexandrux.istrate@intel.com>
  98. AutomatedBy: Daniel Istrate <daniel.alexandrux.istrate@intel.com>
  99. """
  100. test_recipe = 'ed'
  101. # Since we need gpg but we can't use gpg-native for sstate signatures, we
  102. # build gpg-native in our original builddir then run the tests in a second one.
  103. builddir = os.environ.get('BUILDDIR') + "-testsign"
  104. sstatedir = os.path.join(builddir, 'test-sstate')
  105. nsysroot = self.setup_gpg()
  106. feature = 'SSTATE_SIG_KEY ?= "testuser"\n'
  107. feature += 'SSTATE_SIG_PASSPHRASE ?= "test123"\n'
  108. feature += 'SSTATE_VERIFY_SIG ?= "1"\n'
  109. feature += 'GPG_PATH = "%s"\n' % self.gpg_dir
  110. feature += 'SSTATE_DIR = "%s"\n' % sstatedir
  111. # Any mirror might have partial sstate without .sig files, triggering failures
  112. feature += 'SSTATE_MIRRORS_forcevariable = ""\n'
  113. self.write_config(feature)
  114. with self.create_new_builddir(os.environ['BUILDDIR'], builddir):
  115. os.environ["PATH"] = nsysroot + ":" + os.environ["PATH"]
  116. self.add_command_to_tearDown('bitbake -c clean %s' % test_recipe)
  117. self.add_command_to_tearDown('rm -rf %s' % sstatedir)
  118. self.add_command_to_tearDown('rm -rf %s' % builddir)
  119. bitbake('-c clean %s' % test_recipe)
  120. bitbake('-c populate_lic %s' % test_recipe)
  121. recipe_sig = glob.glob(sstatedir + '/*/*:ed:*_populate_lic.tgz.sig')
  122. recipe_tgz = glob.glob(sstatedir + '/*/*:ed:*_populate_lic.tgz')
  123. self.assertEqual(len(recipe_sig), 1, 'Failed to find .sig file.')
  124. self.assertEqual(len(recipe_tgz), 1, 'Failed to find .tgz file.')
  125. ret = runCmd('gpg --homedir %s --verify %s %s' % (self.gpg_dir, recipe_sig[0], recipe_tgz[0]))
  126. # gpg: Signature made Thu 22 Oct 2015 01:45:09 PM EEST using RSA key ID 61EEFB30
  127. # gpg: Good signature from "testuser (nocomment) <testuser@email.com>"
  128. self.assertIn('gpg: Good signature from', ret.output, 'Package signed incorrectly.')
  129. class LockedSignatures(OESelftestTestCase):
  130. @OETestID(1420)
  131. def test_locked_signatures(self):
  132. """
  133. Summary: Test locked signature mechanism
  134. Expected: Locked signatures will prevent task to run
  135. Product: oe-core
  136. Author: Daniel Istrate <daniel.alexandrux.istrate@intel.com>
  137. AutomatedBy: Daniel Istrate <daniel.alexandrux.istrate@intel.com>
  138. """
  139. test_recipe = 'ed'
  140. locked_sigs_file = 'locked-sigs.inc'
  141. self.add_command_to_tearDown('rm -f %s' % os.path.join(self.builddir, locked_sigs_file))
  142. bitbake(test_recipe)
  143. # Generate locked sigs include file
  144. bitbake('-S none %s' % test_recipe)
  145. feature = 'require %s\n' % locked_sigs_file
  146. feature += 'SIGGEN_LOCKEDSIGS_TASKSIG_CHECK = "warn"\n'
  147. self.write_config(feature)
  148. # Build a locked recipe
  149. bitbake(test_recipe)
  150. # Make a change that should cause the locked task signature to change
  151. recipe_append_file = test_recipe + '_' + get_bb_var('PV', test_recipe) + '.bbappend'
  152. recipe_append_path = os.path.join(self.testlayer_path, 'recipes-test', test_recipe, recipe_append_file)
  153. feature = 'SUMMARY += "test locked signature"\n'
  154. os.mkdir(os.path.join(self.testlayer_path, 'recipes-test', test_recipe))
  155. write_file(recipe_append_path, feature)
  156. self.add_command_to_tearDown('rm -rf %s' % os.path.join(self.testlayer_path, 'recipes-test', test_recipe))
  157. # Build the recipe again
  158. ret = bitbake(test_recipe)
  159. # Verify you get the warning and that the real task *isn't* run (i.e. the locked signature has worked)
  160. patt = r'WARNING: The %s:do_package sig is computed to be \S+, but the sig is locked to \S+ in SIGGEN_LOCKEDSIGS\S+' % test_recipe
  161. found_warn = re.search(patt, ret.output)
  162. self.assertIsNotNone(found_warn, "Didn't find the expected warning message. Output: %s" % ret.output)