2015-03-12 10:52:33 -05:00
|
|
|
# -*- 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
|
|
|
|
|
2016-11-22 10:55:10 -06:00
|
|
|
from ipaserver import p11helper as _ipap11helper
|
2015-03-12 10:52:33 -05:00
|
|
|
|
2015-04-24 07:39:48 -05:00
|
|
|
pytestmark = pytest.mark.tier0
|
2015-03-12 10:52:33 -05:00
|
|
|
|
|
|
|
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")
|
2018-04-05 06:12:59 -05:00
|
|
|
def token_path():
|
2015-03-12 10:52:33 -05:00
|
|
|
token_path = tempfile.mkdtemp(prefix='pytest_', suffix='_pkcs11')
|
2018-04-05 06:12:59 -05:00
|
|
|
os.mkdir(os.path.join(token_path, 'tokens'))
|
|
|
|
return token_path
|
2015-03-12 10:52:33 -05:00
|
|
|
|
2018-04-05 06:12:59 -05:00
|
|
|
|
|
|
|
@pytest.fixture(scope="module")
|
|
|
|
def p11(request, token_path):
|
|
|
|
with open(os.path.join(token_path, 'softhsm2.conf'), 'w') as cfg:
|
2015-03-12 10:52:33 -05:00
|
|
|
cfg.write(CONFIG_DATA % token_path)
|
2018-04-05 06:12:59 -05:00
|
|
|
|
|
|
|
args = [
|
|
|
|
SOFTHSM2_UTIL, '--init-token', '--free',
|
|
|
|
'--label', 'test',
|
|
|
|
'--pin', '1234',
|
|
|
|
'--so-pin', '1234'
|
|
|
|
]
|
2015-03-12 10:52:33 -05:00
|
|
|
os.environ['SOFTHSM2_CONF'] = os.path.join(token_path, 'softhsm2.conf')
|
2018-04-05 06:12:59 -05:00
|
|
|
subprocess.check_call(args, cwd=token_path)
|
2015-03-12 10:52:33 -05:00
|
|
|
|
|
|
|
try:
|
2017-03-29 11:53:11 -05:00
|
|
|
p11 = _ipap11helper.P11_Helper('test', "1234", LIBSOFTHSM)
|
2015-03-12 10:52:33 -05:00
|
|
|
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:
|
2017-03-29 11:53:11 -05:00
|
|
|
subprocess.call(
|
2018-04-05 06:12:59 -05:00
|
|
|
[SOFTHSM2_UTIL, '--delete-token', '--label', 'test'],
|
|
|
|
cwd=token_path
|
|
|
|
)
|
2015-03-12 10:52:33 -05:00
|
|
|
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)
|
|
|
|
|
2018-04-05 06:12:59 -05:00
|
|
|
def test_export_import_of_public_key(self, p11, token_path):
|
2015-03-12 10:52:33 -05:00
|
|
|
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))
|
2018-04-05 06:12:59 -05:00
|
|
|
pubfile = os.path.join(token_path, "public_key.asn1.der")
|
|
|
|
with open(pubfile, "wb") as f:
|
2015-03-12 10:52:33 -05:00
|
|
|
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
|
|
|
|
|
2018-04-05 06:12:59 -05:00
|
|
|
def test_wrap_unwrap_key_by_master_key_with_AES(self, p11, token_path):
|
2015-03-12 10:52:33 -05:00
|
|
|
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))
|
2018-04-05 06:12:59 -05:00
|
|
|
privfile = os.path.join(token_path, "wrapped_priv.der")
|
|
|
|
with open(privfile, "wb") as f:
|
2015-03-12 10:52:33 -05:00
|
|
|
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.")
|