mirror of
https://salsa.debian.org/freeipa-team/freeipa.git
synced 2024-12-26 00:41:25 -06:00
e8f2a415b3
Slots in HSM are not assigned statically, we have to chose proper slot from token label. Softhsm i2.2.0 changed this behavior and now slots can change over time (it is allowed by pkcs11 standard). Changelog: * created method get_slot() that returns slot number from used label * replaces usage of slot in __init__ method of P11_Helper with label * slot is dynamically detected from token label before session is opened * pkcs11-util --init-token now uses '--free' instead '--slot' which uses first free slot (we don't care about slot numbers anymore) https://pagure.io/freeipa/issue/6692 Reviewed-By: Stanislav Laznicka <slaznick@redhat.com>
280 lines
12 KiB
Python
280 lines
12 KiB
Python
# -*- coding: utf-8 -*-
|
|
#
|
|
# Copyright (C) 2015 FreeIPA Contributors see COPYING for license
|
|
#
|
|
"""
|
|
Test the `ipapython/ipap11helper/p11helper.c` module.
|
|
"""
|
|
|
|
|
|
from binascii import hexlify
|
|
import os
|
|
import os.path
|
|
import logging
|
|
import subprocess
|
|
import tempfile
|
|
|
|
import pytest
|
|
from ipaplatform.paths import paths
|
|
|
|
from ipaserver import p11helper as _ipap11helper
|
|
|
|
pytestmark = pytest.mark.tier0
|
|
|
|
CONFIG_DATA = """
|
|
# SoftHSM v2 configuration file
|
|
directories.tokendir = %s/tokens
|
|
objectstore.backend = file
|
|
"""
|
|
|
|
LIBSOFTHSM = paths.LIBSOFTHSM2_SO
|
|
SOFTHSM2_UTIL = paths.SOFTHSM2_UTIL
|
|
|
|
logging.basicConfig(level=logging.INFO)
|
|
log = logging.getLogger('t')
|
|
|
|
|
|
master_key_label = u"master-ž" # random non-ascii character to test unicode
|
|
master_key_id = "m"
|
|
replica1_key_label = u"replica1"
|
|
replica1_key_id = "id1"
|
|
replica1_import_label = u"replica1-import"
|
|
replica1_import_id = "id1-import"
|
|
replica1_new_label = u"replica1-new-label-ž"
|
|
replica2_key_label = u"replica2"
|
|
replica2_key_id = "id2"
|
|
replica_non_existent_label = u"replica-nonexistent"
|
|
|
|
|
|
@pytest.fixture(scope="module")
|
|
def p11(request):
|
|
token_path = tempfile.mkdtemp(prefix='pytest_', suffix='_pkcs11')
|
|
os.chdir(token_path)
|
|
os.mkdir('tokens')
|
|
|
|
with open('softhsm2.conf', 'w') as cfg:
|
|
cfg.write(CONFIG_DATA % token_path)
|
|
os.environ['SOFTHSM2_CONF'] = os.path.join(token_path, 'softhsm2.conf')
|
|
subprocess.check_call([SOFTHSM2_UTIL, '--init-token', '--free',
|
|
'--label', 'test', '--pin', '1234', '--so-pin',
|
|
'1234'])
|
|
|
|
try:
|
|
p11 = _ipap11helper.P11_Helper('test', "1234", LIBSOFTHSM)
|
|
except _ipap11helper.Error:
|
|
pytest.fail('Failed to initialize the helper object.', pytrace=False)
|
|
|
|
def fin():
|
|
try:
|
|
p11.finalize()
|
|
except _ipap11helper.Error:
|
|
pytest.fail('Failed to finalize the helper object.', pytrace=False)
|
|
finally:
|
|
subprocess.call(
|
|
[SOFTHSM2_UTIL, '--delete-token', '--label', 'test'])
|
|
del os.environ['SOFTHSM2_CONF']
|
|
|
|
request.addfinalizer(fin)
|
|
|
|
return p11
|
|
|
|
|
|
class test_p11helper(object):
|
|
def test_generate_master_key(self, p11):
|
|
assert p11.generate_master_key(master_key_label, master_key_id,
|
|
key_length=16, cka_wrap=True,
|
|
cka_unwrap=True)
|
|
|
|
def test_search_for_master_key(self, p11):
|
|
master_key = p11.find_keys(_ipap11helper.KEY_CLASS_SECRET_KEY,
|
|
label=master_key_label, id=master_key_id)
|
|
assert len(master_key) == 1, "The master key should exist."
|
|
|
|
def test_generate_replica_key_pair(self, p11):
|
|
assert p11.generate_replica_key_pair(replica1_key_label,
|
|
replica1_key_id,
|
|
pub_cka_wrap=True,
|
|
priv_cka_unwrap=True)
|
|
|
|
def test_find_key(self, p11):
|
|
rep1_pub = p11.find_keys(_ipap11helper.KEY_CLASS_PUBLIC_KEY,
|
|
label=replica1_key_label, cka_wrap=True)
|
|
assert len(rep1_pub) == 1, ("replica key pair has to contain "
|
|
"1 pub key instead of %s" % len(rep1_pub))
|
|
|
|
rep1_priv = p11.find_keys(_ipap11helper.KEY_CLASS_PRIVATE_KEY,
|
|
label=replica1_key_label, cka_unwrap=True)
|
|
assert len(rep1_priv) == 1, ("replica key pair has to contain 1 "
|
|
"private key instead of %s" %
|
|
len(rep1_priv))
|
|
|
|
def test_find_key_by_uri(self, p11):
|
|
rep1_pub = p11.find_keys(uri="pkcs11:object=replica1;objecttype=public")
|
|
assert len(rep1_pub) == 1, ("replica key pair has to contain 1 pub "
|
|
"key instead of %s" % len(rep1_pub))
|
|
|
|
def test_get_attribute_from_object(self, p11):
|
|
rep1_pub = p11.find_keys(_ipap11helper.KEY_CLASS_PUBLIC_KEY,
|
|
label=replica1_key_label, cka_wrap=True)[0]
|
|
|
|
iswrap = p11.get_attribute(rep1_pub, _ipap11helper.CKA_WRAP)
|
|
assert iswrap is True, "replica public key has to have CKA_WRAP = TRUE"
|
|
|
|
def test_generate_replica_keypair_with_extractable_private_key(self, p11):
|
|
assert p11.generate_replica_key_pair(replica2_key_label,
|
|
replica2_key_id,
|
|
pub_cka_wrap=True,
|
|
priv_cka_unwrap=True,
|
|
priv_cka_extractable=True)
|
|
|
|
def test_find_key_on_nonexistent_key_pair(self, p11):
|
|
test_list = p11.find_keys(_ipap11helper.KEY_CLASS_PUBLIC_KEY,
|
|
label=replica_non_existent_label)
|
|
assert len(test_list) == 0, ("list should be empty because label "
|
|
"'%s' should not exist" %
|
|
replica_non_existent_label)
|
|
|
|
def test_export_import_of_public_key(self, p11):
|
|
rep1_pub = p11.find_keys(_ipap11helper.KEY_CLASS_PUBLIC_KEY,
|
|
label=replica1_key_label, cka_wrap=True)[0]
|
|
pub = p11.export_public_key(rep1_pub)
|
|
|
|
log.debug("Exported public key %s", hexlify(pub))
|
|
with open("public_key.asn1.der", "wb") as f:
|
|
f.write(pub)
|
|
|
|
rep1_pub_import = p11.import_public_key(replica1_import_label,
|
|
replica1_import_id,
|
|
pub,
|
|
cka_wrap=True)
|
|
log.debug('imported replica 1 public key: %s', rep1_pub_import)
|
|
|
|
# test public key import
|
|
rep1_modulus_orig = p11.get_attribute(rep1_pub,
|
|
_ipap11helper.CKA_MODULUS)
|
|
rep1_modulus_import = p11.get_attribute(rep1_pub_import,
|
|
_ipap11helper.CKA_MODULUS)
|
|
log.debug('rep1_modulus_orig = 0x%s', hexlify(rep1_modulus_orig))
|
|
log.debug('rep1_modulus_import = 0x%s', hexlify(rep1_modulus_import))
|
|
assert rep1_modulus_import == rep1_modulus_orig
|
|
|
|
rep1_pub_exp_orig = p11.get_attribute(
|
|
rep1_pub, _ipap11helper.CKA_PUBLIC_EXPONENT)
|
|
rep1_pub_exp_import = p11.get_attribute(
|
|
rep1_pub_import, _ipap11helper.CKA_PUBLIC_EXPONENT)
|
|
log.debug('rep1_pub_exp_orig = 0x%s', hexlify(rep1_pub_exp_orig))
|
|
log.debug('rep1_pub_exp_import = 0x%s', hexlify(rep1_pub_exp_import))
|
|
assert rep1_pub_exp_import == rep1_pub_exp_orig
|
|
|
|
def test_wrap_unwrap_key_by_master_key_with_AES(self, p11):
|
|
master_key = p11.find_keys(_ipap11helper.KEY_CLASS_SECRET_KEY,
|
|
label=master_key_label, id=master_key_id)[0]
|
|
rep2_priv = p11.find_keys(_ipap11helper.KEY_CLASS_PRIVATE_KEY,
|
|
label=replica2_key_label, cka_unwrap=True)[0]
|
|
|
|
log.debug("wrapping dnssec priv key by master key")
|
|
wrapped_priv = p11.export_wrapped_key(
|
|
rep2_priv, master_key, _ipap11helper.MECH_AES_KEY_WRAP_PAD
|
|
)
|
|
assert wrapped_priv
|
|
|
|
log.debug("wrapped_dnssec priv key: %s", hexlify(wrapped_priv))
|
|
with open("wrapped_priv.der", "wb") as f:
|
|
f.write(wrapped_priv)
|
|
|
|
assert p11.import_wrapped_private_key(
|
|
u'test_import_wrapped_priv',
|
|
'1',
|
|
wrapped_priv,
|
|
master_key,
|
|
_ipap11helper.MECH_AES_KEY_WRAP_PAD,
|
|
_ipap11helper.KEY_TYPE_RSA
|
|
)
|
|
|
|
def test_wrap_unwrap_key_by_master_key_with_RSA_PKCS(self, p11):
|
|
master_key = p11.find_keys(_ipap11helper.KEY_CLASS_SECRET_KEY,
|
|
label=master_key_label, id=master_key_id)[0]
|
|
rep2_pub = p11.find_keys(_ipap11helper.KEY_CLASS_PUBLIC_KEY,
|
|
label=replica2_key_label, cka_wrap=True)[0]
|
|
rep2_priv = p11.find_keys(_ipap11helper.KEY_CLASS_PRIVATE_KEY,
|
|
label=replica2_key_label, cka_unwrap=True)[0]
|
|
|
|
wrapped = p11.export_wrapped_key(master_key,
|
|
rep2_pub,
|
|
_ipap11helper.MECH_RSA_PKCS)
|
|
assert wrapped
|
|
|
|
log.debug("wrapped key MECH_RSA_PKCS (secret master wrapped by pub "
|
|
"key): %s", hexlify(wrapped))
|
|
assert p11.import_wrapped_secret_key(u'test_import_wrapped',
|
|
'2',
|
|
wrapped,
|
|
rep2_priv,
|
|
_ipap11helper.MECH_RSA_PKCS,
|
|
_ipap11helper.KEY_TYPE_AES)
|
|
|
|
def test_wrap_unwrap_by_master_key_with_RSA_PKCS_OAEP(self, p11):
|
|
master_key = p11.find_keys(_ipap11helper.KEY_CLASS_SECRET_KEY,
|
|
label=master_key_label, id=master_key_id)[0]
|
|
rep2_pub = p11.find_keys(_ipap11helper.KEY_CLASS_PUBLIC_KEY,
|
|
label=replica2_key_label, cka_wrap=True)[0]
|
|
rep2_priv = p11.find_keys(_ipap11helper.KEY_CLASS_PRIVATE_KEY,
|
|
label=replica2_key_label, cka_unwrap=True)[0]
|
|
|
|
wrapped = p11.export_wrapped_key(master_key,
|
|
rep2_pub,
|
|
_ipap11helper.MECH_RSA_PKCS_OAEP)
|
|
assert wrapped
|
|
|
|
log.debug("wrapped key MECH_RSA_PKCS_OAEP (secret master wrapped by "
|
|
"pub key): %s", hexlify(wrapped))
|
|
|
|
assert p11.import_wrapped_secret_key(u'test_import_wrapped',
|
|
'3',
|
|
wrapped,
|
|
rep2_priv,
|
|
_ipap11helper.MECH_RSA_PKCS_OAEP,
|
|
_ipap11helper.KEY_TYPE_AES)
|
|
|
|
def test_set_attribute_on_object(self, p11):
|
|
rep1_pub = p11.find_keys(_ipap11helper.KEY_CLASS_PUBLIC_KEY,
|
|
label=replica1_key_label, cka_wrap=True)[0]
|
|
test_label = replica1_new_label
|
|
|
|
p11.set_attribute(rep1_pub, _ipap11helper.CKA_LABEL, test_label)
|
|
assert p11.get_attribute(rep1_pub, _ipap11helper.CKA_LABEL) \
|
|
== test_label, "The labels do not match."
|
|
|
|
def test_do_not_generate_identical_master_keys(self, p11):
|
|
with pytest.raises(_ipap11helper.DuplicationError):
|
|
p11.generate_master_key(master_key_label, master_key_id,
|
|
key_length=16)
|
|
|
|
master_key = p11.find_keys(_ipap11helper.KEY_CLASS_SECRET_KEY,
|
|
label=master_key_label)
|
|
assert len(master_key) == 1, ("There shouldn't be multiple keys "
|
|
"with the same label.")
|
|
|
|
def test_delete_key(self, p11):
|
|
master_key = p11.find_keys(_ipap11helper.KEY_CLASS_SECRET_KEY,
|
|
label=master_key_label, id=master_key_id)[0]
|
|
rep1_pub = p11.find_keys(_ipap11helper.KEY_CLASS_PUBLIC_KEY,
|
|
label=replica1_new_label, cka_wrap=True)[0]
|
|
rep2_priv = p11.find_keys(_ipap11helper.KEY_CLASS_PRIVATE_KEY,
|
|
label=replica2_key_label, cka_unwrap=True)[0]
|
|
|
|
for key in (rep1_pub, rep2_priv, master_key):
|
|
p11.delete_key(key)
|
|
|
|
master_key = p11.find_keys(_ipap11helper.KEY_CLASS_SECRET_KEY,
|
|
label=master_key_label, id=master_key_id)
|
|
assert len(master_key) == 0, "The master key should be deleted."
|
|
rep1_pub = p11.find_keys(_ipap11helper.KEY_CLASS_PUBLIC_KEY,
|
|
label=replica1_new_label, cka_wrap=True)
|
|
assert len(rep1_pub) == 0, ("The public key of replica1 pair should "
|
|
"be deleted.")
|
|
rep2_priv = p11.find_keys(_ipap11helper.KEY_CLASS_PRIVATE_KEY,
|
|
label=replica2_key_label, cka_unwrap=True)
|
|
assert len(rep2_priv) == 0, ("The private key of replica2 pair should"
|
|
" be deleted.")
|