blob: f37a0590a28939456156049fa0abb90de2e93ade [file] [log] [blame]
# -*- coding: utf-8 -*-
# Copyright 2018 The Chromium OS Authors. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
""""ChromeOS key unittests"""
from __future__ import print_function
import os
import textwrap
from chromite.lib import cros_test_lib
from chromite.lib import osutils
from chromite.lib import partial_mock
from chromite.signing.lib import keys
MOCK_SHA1SUM = 'e2c1c92d7d7aa7dfed5e8375edd30b7ae52b7450'
def MockVbutilKey(rc, sha1sum=MOCK_SHA1SUM):
"""Adds vbutil_key mocks to |rc|"""
cmd_output = textwrap.dedent('''
Public Key file: firmware_data_key.vbpubk
Algorithm: 7 RSA4096 SHA256
Key Version: 1
Key sha1sum: ''' + sha1sum)
rc.AddCmdResult(partial_mock.ListRegex('vbutil_key --unpack .*'),
output=cmd_output)
class KeysetMock(keys.Keyset):
"""Mock Keyset that mimics common loem key directory."""
KEYS = ('ec_data_key',
'ec_root_key',
'key_ec_efs',
'firmware_data_key',
'installer_kernel_data_key',
'kernel_data_key',
'kernel_subkey',
'recovery_kernel_data_key',
'recovery_key',
'root_key')
KEYS_WITH_SUBKEYS = ('firmware_data_key', 'root_key')
SUBKEYS = ('loem1', 'loem2', 'loem3', 'loem4')
ALIAS = ('ACME', 'SHINRA')
def __init__(self, keydir):
super(KeysetMock, self).__init__()
self.keydir = keydir
for key_name in KeysetMock.KEYS:
self.AddKey(keys.KeyPair(key_name, keydir))
for key_name in KeysetMock.KEYS_WITH_SUBKEYS:
for subkey_name in KeysetMock.SUBKEYS:
self.AddSubkey(key_name, subkey_name)
for alias, subkey_name in zip(KeysetMock.ALIAS, KeysetMock.SUBKEYS):
self.subkey_aliases[alias] = subkey_name
def WriteIniFile(self):
"""Writes alias to file"""
with open(os.path.join(self.keydir, 'loem.ini'), 'w') as conf:
conf.write('[loem]\n'
'1 = ACME\n'
'2 = SHINRA')
def CreateDummyKeys(self):
"""Creates dummy keys from stored keys."""
for key in self.keys.values():
CreateDummyKeys(key)
class TestKeyPair(cros_test_lib.RunCommandTempDirTestCase):
"""Test KeyPair class."""
def testInitSimple(self):
"""Test init with minimal arguments."""
k1 = keys.KeyPair('key1', self.tempdir)
self.assertEqual(k1.name, 'key1')
self.assertEqual(k1.version, '1')
self.assertEqual(k1.keydir, self.tempdir)
def testPrivateKey(self):
k1 = keys.KeyPair('key1', self.tempdir)
self.assertEqual(k1.private,
os.path.join(self.tempdir, 'key1' + '.vbprivk'))
def testPublicKey(self):
k1 = keys.KeyPair('key1', self.tempdir)
self.assertEqual(k1.public,
os.path.join(self.tempdir, 'key1' + '.vbpubk'))
def testKeyblock(self):
k1 = keys.KeyPair('key1', self.tempdir)
self.assertEqual(k1.keyblock,
os.path.join(self.tempdir, 'key1' + '.keyblock'))
def testKeyblockWithSuffix(self):
k1 = keys.KeyPair('key1_data_key', self.tempdir)
self.assertEqual(k1.keyblock,
os.path.join(self.tempdir, 'key1' + '.keyblock'))
def testInitWithVersion(self):
"""Test init with version kwarg."""
k_ver = keys.KeyPair('k_ver', self.tempdir, version=2)
self.assertEqual(k_ver.version, 2)
def testInitWithPubExt(self):
"""Test init with pub_ext kwarg."""
k_ext = keys.KeyPair('k_ext', self.tempdir, pub_ext='.vbpubk2')
self.assertEqual(k_ext.public,
os.path.join(self.tempdir, 'k_ext.vbpubk2'))
def testInitWithPrivExt(self):
"""Test init with priv_ext kwarg."""
k_ext = keys.KeyPair('k_ext', self.tempdir, priv_ext='.vbprivk2')
self.assertEqual(k_ext.private,
os.path.join(self.tempdir, 'k_ext.vbprivk2'))
def testCmpSame(self):
k1 = keys.KeyPair('key1', self.tempdir)
k2 = keys.KeyPair('key1', self.tempdir)
self.assertEqual(k1, k1)
self.assertEqual(k1, k2)
def testCmpDiff(self):
k1 = keys.KeyPair('key1', self.tempdir)
k2 = keys.KeyPair('key2', self.tempdir)
self.assertNotEqual(k1, k2)
def testAddSubkey(self):
k1 = keys.KeyPair('key1', self.tempdir)
k1.AddSubkey('loem1')
k_loem1 = k1.subkeys['loem1']
self.assertIsInstance(k_loem1, keys.KeyPair)
self.assertEqual(k_loem1.name, 'key1.loem1')
self.assertEqual(k1.keydir, k_loem1.keydir)
self.assertEqual(k1.version, k_loem1.version)
def testExistsEmpty(self):
self.assertFalse(keys.KeyPair('key1', self.tempdir).Exists())
def testExistEmptyRequirePublic(self):
k1 = keys.KeyPair('key1', self.tempdir)
self.assertFalse(k1.Exists(require_public=True))
def testExistEmptyRequirePrivate(self):
k1 = keys.KeyPair('key1', self.tempdir)
self.assertFalse(k1.Exists(require_private=True))
def testExistEmptyRequirePublicRequirePrivate(self):
k1 = keys.KeyPair('key1', self.tempdir)
self.assertFalse(k1.Exists(require_private=True, require_public=True))
def testExistWithPublicKey(self):
k1 = keys.KeyPair('key1', self.tempdir)
CreateDummyPublic(k1)
self.assertTrue(k1.Exists())
self.assertTrue(k1.Exists(require_public=True))
self.assertFalse(k1.Exists(require_private=True))
def testExistsWithPrivateKey(self):
k1 = keys.KeyPair('key1', self.tempdir)
CreateDummyPrivateKey(k1)
self.assertTrue(k1.Exists())
self.assertTrue(k1.Exists(require_private=True))
self.assertFalse(k1.Exists(require_public=True))
def testKeyblockExistsMissing(self):
k1 = keys.KeyPair('key1', self.tempdir)
self.assertFalse(k1.KeyblockExists())
def testKeyblockExists(self):
k1 = keys.KeyPair('key1', self.tempdir)
CreateDummyKeyblock(k1)
self.assertTrue(k1.KeyblockExists())
def testGetSha1sumEmpty(self):
""""Test GetSha1sum with bad cmd output."""
k1 = keys.KeyPair('key1', self.tempdir)
with self.assertRaises(keys.SignerKeyError):
k1.GetSHA1sum()
def testGetSha1sumMockCmd(self):
"""Test GetSha1sum with mock cmd output."""
MockVbutilKey(self.rc)
k1 = keys.KeyPair('firmware_data_key', self.tempdir)
k1sum = k1.GetSHA1sum()
self.assertEqual(k1sum, MOCK_SHA1SUM)
self.assertCommandCalled(['vbutil_key', '--unpack', k1.public],
error_code_ok=True)
class TestKeyset(cros_test_lib.TempDirTestCase):
"""Test Keyset class."""
def _get_keyset(self):
"""Returns keyset with a few keys."""
kc = keys.Keyset()
for key_name in ['key1', 'key2', 'key3', 'key4']:
kc.AddKey(keys.KeyPair(key_name, self.tempdir))
kc.subkey_aliases = {'board1':'loem1',
'board2':'loem2'}
for subkey in kc.subkey_aliases:
kc.AddSubkey('key4', subkey)
return kc
def testInit(self):
ks = keys.Keyset()
self.assertIsInstance(ks.keys, dict)
self.assertIsInstance(ks.subkey_aliases, dict)
def testEqSame(self):
kc1 = self._get_keyset()
kc2 = self._get_keyset()
self.assertEqual(kc1, kc2)
def testEqDiffrent(self):
kc1 = self._get_keyset()
kc2 = self._get_keyset()
kc2 = self._get_keyset()
kc2.AddKey(keys.KeyPair('Foo', self.tempdir))
self.assertFalse(kc1 == kc2)
def testAddKey(self):
ks0 = keys.Keyset()
key0 = keys.KeyPair('key0', self.tempdir)
ks0.AddKey(key0)
self.assertEqual(ks0.keys['key0'], key0)
def testAddKeyWithKeyName(self):
ks0 = keys.Keyset()
key0 = keys.KeyPair('key0', self.tempdir)
ks0.AddKey(key0, key_name='key0_new')
self.assertEqual(ks0.keys['key0_new'], key0)
def testAddSubkey(self):
ks0 = self._get_keyset()
ks0.AddSubkey('key1', 'sub1')
self.assertIsInstance(ks0.keys['key1'].subkeys['sub1'], keys.KeyPair)
def testAddSubkeyWithAlias(self):
ks0 = self._get_keyset()
ks0.subkey_aliases['sub1'] = 'loem1'
ks0.AddSubkey('key1', 'sub1')
self.assertIsInstance(ks0.keys['key1'].subkeys['loem1'], keys.KeyPair)
def testGetSubKeysetMissmatch(self):
ks0 = self._get_keyset()
with self.assertRaises(keys.SignerSubkeyMissingError):
ks0.GetSubKeyset('foo')
def testGetSubKeyset(self):
ks0 = self._get_keyset()
ks1 = ks0.GetSubKeyset('loem1')
self.assertEqual(ks0.keys['key4'].subkeys['loem1'], ks1.keys['key4'])
def testGetSubKeysetWithAliases(self):
ks0 = self._get_keyset()
ks1 = ks0.GetSubKeyset('board2')
self.assertEqual(ks0.keys['key4'].subkeys['loem2'], ks1.keys['key4'])
def testPrune(self):
ks0 = self._get_keyset()
key_keep = ['key1', 'key3']
for key_name in key_keep:
CreateDummyKeys(ks0.keys[key_name])
ks0.Prune()
for key_name, key in ks0.keys.items():
self.assertTrue(key.Exists(), msg='All keys should exist')
self.assertIn(key_name, key_keep,
msg='Only keys in key_keep should exists')
def testKeyExistsMissing(self):
ks0 = self._get_keyset()
self.assertFalse(ks0.KeyExists('foo'), msg="'foo' should not exist")
self.assertFalse(ks0.KeyExists('key1'), msg="'key1' should not exist")
def testKeyExistsPublicAndPrivate(self):
ks0 = self._get_keyset()
CreateDummyKeys(ks0.keys['key1'])
self.assertTrue(ks0.KeyExists('key1'), msg="key1 should exist")
self.assertTrue(ks0.KeyExists('key1', require_public=True),
msg="key1 public key should exist")
self.assertTrue(ks0.KeyExists('key1', require_private=True),
msg="key1 private key should exist")
self.assertTrue(ks0.KeyExists('key1',
require_public=True,
require_private=True),
msg="key1 keys should exist")
def testKeyExistsPrivate(self):
ks0 = self._get_keyset()
CreateDummyPrivateKey(ks0.keys['key2'])
self.assertTrue(ks0.KeyExists('key2'),
msg="should pass with only private key")
self.assertTrue(ks0.KeyExists('key2', require_private=True),
msg="Should exist with only private key")
self.assertFalse(ks0.KeyExists('key2', require_public=True),
msg="Shouldn't pass with only private key")
def testKeyExistsPublic(self):
ks0 = self._get_keyset()
CreateDummyPublic(ks0.keys['key3'])
self.assertTrue(ks0.KeyExists('key3'),
msg="should pass with only public key")
self.assertTrue(ks0.KeyExists('key3', require_public=True),
msg="Should exist with only public key")
self.assertFalse(ks0.KeyExists('key3', require_private=True),
msg="Shouldn't pass with only public key")
def testKeyblockExistsMissing(self):
ks0 = self._get_keyset()
self.assertFalse(ks0.KeyExists('foo'), msg="'foo' should not exist")
self.assertFalse(ks0.KeyExists('key1'), msg="'key1' not created yet")
def testKeyblockExists(self):
ks0 = self._get_keyset()
CreateDummyKeyblock(ks0.keys['key1'])
self.assertTrue(ks0.KeyblockExists('key1'), msg="'key1' should exist")
class testKeysetFromDir(cros_test_lib.TempDirTestCase):
"""Test keyeset_from_dir function."""
def testEmpty(self):
ks = keys.KeysetFromDir(self.tempdir)
self.assertIsInstance(ks, keys.Keyset)
def testWithKeysetMock(self):
ks0 = KeysetMock(self.tempdir)
ks0.CreateDummyKeys()
ks0.WriteIniFile()
ks1 = keys.KeysetFromDir(self.tempdir)
self.assertDictEqual(ks0.keys, ks1.keys, msg='Incorrect keys')
for key in KeysetMock.KEYS_WITH_SUBKEYS:
self.assertDictEqual(ks0.keys[key].subkeys, ks1.keys[key].subkeys,
msg='Incorrect subkeys')
self.assertDictEqual(ks0.subkey_aliases, ks1.subkey_aliases,
msg='Incorrect key aliases')
self.assertEqual(ks0, ks1)
def CreateDummyPublic(key):
"""Create empty public key file for given key (or subkeys if exist)."""
if key.subkeys:
for subkey in key.subkeys.values():
CreateDummyPublic(subkey)
else:
osutils.Touch(key.public, makedirs=True)
def CreateDummyPrivateKey(key):
"""Create empty private key for given key (or subkeys if exist)."""
if key.subkeys:
for subkey in key.subkeys.values():
CreateDummyPrivateKey(subkey)
else:
osutils.Touch(key.private, makedirs=True)
def CreateDummyKeyblock(key):
"""Create empty keyblock file for given key (or subkeys if exist)."""
if key.subkeys:
for subkey in key.subkeys.values():
CreateDummyKeyblock(subkey)
else:
osutils.Touch(key.keyblock, makedirs=True)
def CreateDummyKeys(key):
"""Create empty key files for given key (or subkeys if exist)."""
CreateDummyPublic(key)
CreateDummyPrivateKey(key)
CreateDummyKeyblock(key)