diff options
-rw-r--r-- | .gitignore | 1 | ||||
-rw-r--r-- | freeipa.spec.in | 12 | ||||
-rw-r--r-- | ipapython/Makefile | 2 | ||||
-rw-r--r-- | ipapython/dnssec/abshsm.py | 2 | ||||
-rw-r--r-- | ipapython/dnssec/ldapkeydb.py | 2 | ||||
-rwxr-xr-x | ipapython/dnssec/localhsm.py | 4 | ||||
-rw-r--r-- | ipapython/ipap11helper/Makefile | 19 | ||||
-rw-r--r-- | ipapython/ipap11helper/library.c | 87 | ||||
-rw-r--r-- | ipapython/ipap11helper/library.h | 48 | ||||
-rw-r--r-- | ipapython/ipap11helper/p11helper.c | 2268 | ||||
-rw-r--r-- | ipapython/ipap11helper/setup.py | 43 | ||||
-rw-r--r-- | ipapython/p11helper.py | 1870 | ||||
-rw-r--r-- | ipaserver/install/dnskeysyncinstance.py | 2 | ||||
-rw-r--r-- | ipaserver/install/opendnssecinstance.py | 2 | ||||
-rw-r--r-- | ipatests/pytest.ini | 1 | ||||
-rw-r--r-- | ipatests/test_ipapython/test_ipap11helper.py | 2 |
16 files changed, 1873 insertions, 2492 deletions
diff --git a/.gitignore b/.gitignore index 937559072..e862c08d7 100644 --- a/.gitignore +++ b/.gitignore @@ -73,7 +73,6 @@ freeipa2-dev-doc /ipapython/setup.py /ipapython/version.py !/ipapython/Makefile -!/ipapython/ipap11helper/Makefile /ipaplatform/__init__.py /ipaplatform/setup.py diff --git a/freeipa.spec.in b/freeipa.spec.in index d045aab8f..899af6c92 100644 --- a/freeipa.spec.in +++ b/freeipa.spec.in @@ -93,9 +93,7 @@ BuildRequires: systemd BuildRequires: libunistring-devel BuildRequires: python-lesscpy BuildRequires: python-yubico >= 1.2.3 -BuildRequires: softhsm-devel >= 2.0.0rc1-1 BuildRequires: openssl-devel -BuildRequires: p11-kit-devel BuildRequires: pki-base >= 10.2.6 BuildRequires: python-pytest-multihost >= 0.5 BuildRequires: python-pytest-sourceorder @@ -471,6 +469,7 @@ Requires: dbus-python Requires: python-setuptools Requires: python-six Requires: python-jwcrypto +Requires: python-cffi Conflicts: %{alt_name}-python < %{version} @@ -515,6 +514,7 @@ Requires: python3-dbus Requires: python3-setuptools Requires: python3-six Requires: python3-jwcrypto +Requires: python3-cffi %description -n python3-ipalib IPA is an integrated solution to provide centrally managed Identity (users, @@ -639,10 +639,6 @@ cd daemons; ../autogen.sh --prefix=%{_usr} --sysconfdir=%{_sysconfdir} --localst cd install; ../autogen.sh --prefix=%{_usr} --sysconfdir=%{_sysconfdir} --localstatedir=%{_localstatedir} --libdir=%{_libdir} --mandir=%{_mandir}; cd .. %endif # ONLY_CLIENT -%if 0%{?with_python3} -(cd ipapython/ipap11helper && make PYTHON=%{__python3} IPA_VERSION_IS_GIT_SNAPSHOT=no %{?_smp_mflags} all) -%endif - %if ! %{ONLY_CLIENT} make IPA_VERSION_IS_GIT_SNAPSHOT=no %{?_smp_mflags} all %else @@ -1311,12 +1307,10 @@ fi %{python_sitelib}/ipalib/* %dir %{python_sitelib}/ipaplatform %{python_sitelib}/ipaplatform/* -%attr(0644,root,root) %{python_sitearch}/_ipap11helper.so %{python_sitelib}/ipapython-*.egg-info %{python_sitelib}/ipalib-*.egg-info %{python_sitelib}/freeipa-*.egg-info %{python_sitelib}/ipaplatform-*.egg-info -%{python_sitearch}/_ipap11helper-*.egg-info %files common -f %{gettext_domain}.lang @@ -1338,8 +1332,6 @@ fi %{python3_sitelib}/ipapython-*.egg-info %{python3_sitelib}/ipalib-*.egg-info %{python3_sitelib}/ipaplatform-*.egg-info -%attr(0644,root,root) %{python3_sitearch}/_ipap11helper.cpython-*.so -%{python3_sitearch}/_ipap11helper-*.egg-info %endif # with_python3 diff --git a/ipapython/Makefile b/ipapython/Makefile index 201c5894d..d262439fd 100644 --- a/ipapython/Makefile +++ b/ipapython/Makefile @@ -1,8 +1,6 @@ PYTHON ?= /usr/bin/python2 PYTHONLIBDIR ?= $(shell $(PYTHON) -c "from distutils.sysconfig import *; print(get_python_lib())") -SUBDIRS = ipap11helper - all: @for subdir in $(SUBDIRS); do \ (cd $$subdir && $(MAKE) $@) || exit 1; \ diff --git a/ipapython/dnssec/abshsm.py b/ipapython/dnssec/abshsm.py index 156bcf32c..1533892f8 100644 --- a/ipapython/dnssec/abshsm.py +++ b/ipapython/dnssec/abshsm.py @@ -2,7 +2,7 @@ # Copyright (C) 2014 FreeIPA Contributors see COPYING for license # -import _ipap11helper +from ipapython import p11helper as _ipap11helper attrs_id2name = { #_ipap11helper.CKA_ALLOWED_MECHANISMS: 'ipk11allowedmechanisms', diff --git a/ipapython/dnssec/ldapkeydb.py b/ipapython/dnssec/ldapkeydb.py index 0ee309525..55c09c040 100644 --- a/ipapython/dnssec/ldapkeydb.py +++ b/ipapython/dnssec/ldapkeydb.py @@ -18,7 +18,7 @@ from ipapython.dnssec.abshsm import ( AbstractHSM, bool_attr_names, populate_pkcs11_metadata) -import _ipap11helper +from ipapython import p11helper as _ipap11helper import uuid def uri_escape(val): diff --git a/ipapython/dnssec/localhsm.py b/ipapython/dnssec/localhsm.py index 76ab00444..8f18a45be 100755 --- a/ipapython/dnssec/localhsm.py +++ b/ipapython/dnssec/localhsm.py @@ -13,7 +13,7 @@ from pprint import pprint from ipaplatform.paths import paths -import _ipap11helper +from ipapython import p11helper as _ipap11helper from ipapython.dnssec.abshsm import (attrs_name2id, attrs_id2name, AbstractHSM, keytype_id2name, keytype_name2id, ldap2p11helper_api_params) @@ -65,7 +65,7 @@ class Key(collections.MutableMapping): return self.p11.set_attribute(self.handle, attrs_name2id[key], value) def __delitem__(self, key): - raise _ipap11helper.Exception('__delitem__ is not supported') + raise _ipap11helper.P11HelperException('__delitem__ is not supported') def __iter__(self): """generates list of ipa names of all attributes present in the object""" diff --git a/ipapython/ipap11helper/Makefile b/ipapython/ipap11helper/Makefile deleted file mode 100644 index f66edb82e..000000000 --- a/ipapython/ipap11helper/Makefile +++ /dev/null @@ -1,19 +0,0 @@ -PYTHON ?= /usr/bin/python2 -PYTHONLIBDIR ?= $(shell $(PYTHON) -c "from distutils.sysconfig import *; print(get_python_lib())") - -all: - $(PYTHON) setup.py build - -install: - if [ "$(DESTDIR)" = "" ]; then \ - $(PYTHON) setup.py install; \ - else \ - $(PYTHON) setup.py install --root $(DESTDIR); \ - fi - -clean: - rm -rf build - -distclean: clean - -maintainer-clean: distclean diff --git a/ipapython/ipap11helper/library.c b/ipapython/ipap11helper/library.c deleted file mode 100644 index acae47e5c..000000000 --- a/ipapython/ipap11helper/library.c +++ /dev/null @@ -1,87 +0,0 @@ -/* - * Copyright (C) 2014 FreeIPA Contributors see COPYING for license - * - * This code is based on PKCS#11 code from SoftHSM project: - * https://github.com/opendnssec/SoftHSMv2/ - * Original license follows: - */ -/* - * Copyright (c) 2010 .SE (The Internet Infrastructure Foundation) - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR - * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED - * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY - * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE - * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER - * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR - * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN - * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -/***************************************************************************** - library.c - - Support function for handling PKCS#11 libraries - *****************************************************************************/ - -#include "library.h" - -#include <stdio.h> -#include <stdlib.h> -#include <dlfcn.h> - -// Load the PKCS#11 library -CK_C_GetFunctionList loadLibrary(const char* module, void** moduleHandle) -{ - CK_C_GetFunctionList pGetFunctionList = NULL; - - void* pDynLib = NULL; - - // Load PKCS #11 library - if (module) - { - pDynLib = dlopen(module, RTLD_NOW | RTLD_LOCAL); - } else { - return NULL; - } - - if (pDynLib == NULL) - { - // Failed to load the PKCS #11 library - return NULL; - } - - // Retrieve the entry point for C_GetFunctionList - pGetFunctionList = (CK_C_GetFunctionList) dlsym(pDynLib, "C_GetFunctionList"); - if (pGetFunctionList == NULL) - { - dlclose(pDynLib); - return NULL; - } - - // Store the handle so we can dlclose it later - *moduleHandle = pDynLib; - - return pGetFunctionList; -} - -void unloadLibrary(void* moduleHandle) -{ - if (moduleHandle) - { - dlclose(moduleHandle); - } -} diff --git a/ipapython/ipap11helper/library.h b/ipapython/ipap11helper/library.h deleted file mode 100644 index afcbd9fd2..000000000 --- a/ipapython/ipap11helper/library.h +++ /dev/null @@ -1,48 +0,0 @@ -/* - * Copyright (C) 2014 FreeIPA Contributors see COPYING for license - * - * This code is based on PKCS#11 code from SoftHSM project: - * https://github.com/opendnssec/SoftHSMv2/ - * Original license follows: - */ -/* - * Copyright (c) 2010 .SE (The Internet Infrastructure Foundation) - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR - * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED - * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY - * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE - * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER - * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR - * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN - * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -/***************************************************************************** - library.h - - Support function for handling PKCS#11 libraries - *****************************************************************************/ - -#ifndef _SOFTHSM_V2_BIN_LIBRARY_H -#define _SOFTHSM_V2_BIN_LIBRARY_H - -#include <p11-kit/pkcs11.h> - -CK_C_GetFunctionList loadLibrary(const char* module, void** moduleHandle); -void unloadLibrary(void* moduleHandle); - -#endif // !_SOFTHSM_V2_BIN_LIBRARY_H diff --git a/ipapython/ipap11helper/p11helper.c b/ipapython/ipap11helper/p11helper.c deleted file mode 100644 index 65bfc07ec..000000000 --- a/ipapython/ipap11helper/p11helper.c +++ /dev/null @@ -1,2268 +0,0 @@ -/* - * Copyright (C) 2014 FreeIPA Contributors see COPYING for license - * - * This file includes an "OpenSSL license exception", see the - * COPYING.openssl file for details. - * - * This code is based on PKCS#11 code snippets from NLnetLabs: - * http://www.nlnetlabs.nl/publications/hsm/examples/pkcs11/ - * Original license follows: - */ -/* - * Copyright (c) 2010 .SE (The Internet Infrastructure Foundation) - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR - * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED - * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY - * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE - * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER - * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR - * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN - * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#include <Python.h> -#include "structmember.h" - -#include <openssl/asn1.h> -#include <openssl/x509.h> -#include <openssl/evp.h> -#include <openssl/rsa.h> -#include <openssl/bn.h> -#include <openssl/bio.h> - -#include <p11-kit/pkcs11.h> -#include <p11-kit/uri.h> - -#include "library.h" - -#if PY_MAJOR_VERSION >= 3 -// Python 3 uses "PyLong" as the int implementation -#define PyInt_Check PyLong_Check -#define PyInt_AsUnsignedLongMask PyLong_AsUnsignedLongMask -#endif - -// compat TODO -#define CKM_AES_KEY_WRAP (0x2109) -#define CKM_AES_KEY_WRAP_PAD (0x210a) - -// TODO -#define CKA_COPYABLE (0x0017) - -#define CKG_MGF1_SHA1 (0x00000001) - -#define CKZ_DATA_SPECIFIED (0x00000001) - -struct ck_rsa_pkcs_oaep_params { - CK_MECHANISM_TYPE hash_alg; - unsigned long mgf; - unsigned long source; - void *source_data; - unsigned long source_data_len; -}; - -typedef struct ck_rsa_pkcs_oaep_params CK_RSA_PKCS_OAEP_PARAMS; -typedef struct ck_rsa_pkcs_oaep_params *CK_RSA_PKCS_OAEP_PARAMS_PTR; - - -CK_BBOOL true = CK_TRUE; -CK_BBOOL false = CK_FALSE; - -#define MAX_TEMPLATE_LEN 32 - -/** - * P11_Helper type - */ -typedef struct { - PyObject_HEAD CK_SLOT_ID slot; - CK_FUNCTION_LIST_PTR p11; - CK_SESSION_HANDLE session; - void *module_handle; -} P11_Helper; - -typedef enum { - sec_en_cka_copyable = 0, - sec_en_cka_decrypt = 1, - sec_en_cka_derive = 2, - sec_en_cka_encrypt = 3, - sec_en_cka_extractable = 4, - sec_en_cka_modifiable = 5, - sec_en_cka_private = 6, - sec_en_cka_sensitive = 7, - sec_en_cka_sign = 8, - sec_en_cka_unwrap = 9, - sec_en_cka_verify = 10, - sec_en_cka_wrap = 11, - sec_en_cka_wrap_with_trusted = 12 -} secrect_key_enum; - -typedef enum { - pub_en_cka_copyable = 0, - pub_en_cka_derive = 1, - pub_en_cka_encrypt = 2, - pub_en_cka_modifiable = 3, - pub_en_cka_private = 4, - pub_en_cka_trusted = 5, - pub_en_cka_verify = 6, - pub_en_cka_verify_recover = 7, - pub_en_cka_wrap = 8 -} public_key_enum; - -typedef enum { - priv_en_cka_always_authenticate = 0, - priv_en_cka_copyable = 1, - priv_en_cka_decrypt = 2, - priv_en_cka_derive = 3, - priv_en_cka_extractable = 4, - priv_en_cka_modifiable = 5, - priv_en_cka_private = 6, - priv_en_cka_sensitive = 7, - priv_en_cka_sign = 8, - priv_en_cka_sign_recover = 9, - priv_en_cka_unwrap = 10, - priv_en_cka_wrap_with_trusted = 11 -} private_key_enum; - -typedef struct { - PyObject *py_obj; - CK_BBOOL *bool; -} PyObj2Bool_mapping_t; - -/** - * Constants - */ -static const CK_RSA_PKCS_OAEP_PARAMS CONST_RSA_PKCS_OAEP_PARAMS = { - .hash_alg = CKM_SHA_1, - .mgf = CKG_MGF1_SHA1, - .source = CKZ_DATA_SPECIFIED, - .source_data = NULL, - .source_data_len = 0 -}; - -/** - * ipap11helper Exceptions - */ -static PyObject *ipap11helperException; // parent class for all exceptions - -static PyObject *ipap11helperError; // general error -static PyObject *ipap11helperNotFound; // key not found -static PyObject *ipap11helperDuplicationError; // key already exists - -/*********************************************************************** - * Support functions - */ - -#define GOTO_FAIL \ - do { \ - error = 1; \ - goto final; \ - } while(0); - -CK_BBOOL *pyobj_to_bool(PyObject *pyobj) { - if (PyObject_IsTrue(pyobj)) - return &true; - return &false; - -} - -void convert_py2bool(PyObj2Bool_mapping_t *mapping, int length) { - int i; - for (i = 0; i < length; ++i) { - PyObject *py_obj = mapping[i].py_obj; - if (py_obj != NULL) { - mapping[i].bool = pyobj_to_bool(py_obj); - } - } -} - -PyObject *string_to_pybytes_or_none(const char *str, Py_ssize_t len) { - if (str == NULL) { - Py_RETURN_NONE; - } - return PyBytes_FromStringAndSize(str, len); -} - -/** - * Convert a unicode string to the utf8 encoded char array - * :param unicode: input python unicode object - * :param l length: of returned string - * Returns NULL if an error occurs, else pointer to string - */ -unsigned char *unicode_to_char_array(PyObject *unicode, Py_ssize_t *l) { - unsigned char *result = NULL; - PyObject *utf8_str = PyUnicode_AsUTF8String(unicode); - if (utf8_str == NULL) { - PyErr_SetString(ipap11helperError, "Unable to encode UTF-8"); - return NULL; - } - unsigned char *bytes = (unsigned char *) PyBytes_AS_STRING(utf8_str); - if (bytes == NULL) { - PyErr_SetString(ipap11helperError, "Unable to get bytes from string"); - *l = 0; - } else { - *l = PyBytes_Size(utf8_str); - - /* Copy string first, then DECREF - * https://docs.python.org/2/c-api/string.html#c.PyString_AS_STRING - */ - result = (unsigned char *) PyMem_Malloc((size_t) * l); - if (result == NULL) { - Py_DECREF(utf8_str); - PyErr_NoMemory(); - return NULL; - } else { - memcpy(result, bytes, *l); - } - - } - Py_DECREF(utf8_str); - return result; -} - -/** - * Convert utf-8 encoded char array to unicode object - */ -PyObject *char_array_to_unicode(const char *array, unsigned long l) { - return PyUnicode_DecodeUTF8(array, l, "strict"); -} - -/** - * Tests result value of pkc11 operations - * :return: 1 if everything is ok, 0 if an error occurs and set the error message - */ -int check_return_value(CK_RV rv, const char *message) { - char *errmsg = NULL; - if (rv != CKR_OK) { - if (asprintf - (&errmsg, "Error at %s: 0x%x\n", message, (unsigned int) rv) - == -1) { - PyErr_SetString(ipap11helperError, - "An error occured during error message generation. " - "Please report this problem. Developers will use " - "a crystal ball to find out the root cause."); - return 0; - } - if (errmsg != NULL) { - PyErr_SetString(ipap11helperError, errmsg); - free(errmsg); - } - return 0; - } - return 1; -} - -/** - * Fill template structure with pointers to attributes passed as independent - * variables. - * Variables with NULL values will be omitted from template. - * - * @warning input variables should not be modified when template is in use - */ -int _fill_template_from_parts(CK_ATTRIBUTE_PTR attr, CK_ULONG_PTR template_len, - CK_BYTE_PTR id, CK_ULONG id_len, - CK_BYTE_PTR label, CK_ULONG label_len, - CK_OBJECT_CLASS *class, CK_BBOOL *cka_wrap, - CK_BBOOL *cka_unwrap) { - int cnt = 0; - if (label != NULL) { - attr->type = CKA_LABEL; - attr->pValue = (void *) label; - attr->ulValueLen = label_len; - ++attr; - ++cnt; - assert(cnt < *template_len); - } - if (id != NULL) { - attr->type = CKA_ID; - attr->pValue = (void *) id; - attr->ulValueLen = id_len; - ++attr; - ++cnt; - assert(cnt < *template_len); - } - if (cka_wrap != NULL) { - attr->type = CKA_WRAP; - attr->pValue = (void *) cka_wrap; - attr->ulValueLen = sizeof(CK_BBOOL); - ++attr; - ++cnt; - assert(cnt < *template_len); - } - if (cka_unwrap != NULL) { - attr->type = CKA_UNWRAP; - attr->pValue = (void *) cka_unwrap; - attr->ulValueLen = sizeof(CK_BBOOL); - ++attr; - ++cnt; - assert(cnt < *template_len); - } - - if (class != NULL) { - attr->type = CKA_CLASS; - attr->pValue = (void *) class; - attr->ulValueLen = sizeof(CK_OBJECT_CLASS); - ++attr; - ++cnt; - assert(cnt < *template_len); - } - *template_len = cnt; - return 1; -} - -/** - * Parse string to P11-kit representation of PKCS#11 URI. - * - * @pre *urip is NULL - * @post - * - * @retval 0 in case of error - * @retval 1 when urip is filled with pointer to new URI structure - */ -int _parse_uri(const char *uri_str, P11KitUri **urip) { - P11KitUriResult result; - P11KitUri *uri = NULL; - - assert(urip != NULL && *urip == NULL); - - uri = p11_kit_uri_new(); - if (!uri) { - PyErr_SetString(ipap11helperError, "Cannot initialize URI parser"); - return 0; - } - - result = p11_kit_uri_parse(uri_str, P11_KIT_URI_FOR_OBJECT, uri); - if (result != P11_KIT_URI_OK) { - PyErr_SetString(ipap11helperError, "Cannot parse URI"); - goto cleanup; - } - - if (p11_kit_uri_any_unrecognized(uri)) { - PyErr_SetString(ipap11helperError, "PKCS#11 URI contains " - "unsupported attributes"); - goto cleanup; - } - - *urip = uri; - return 1; - -cleanup: - p11_kit_uri_free(uri); - return 0; -} - -/* - * Find keys matching specified template. - * Function returns list of key handles via objects parameter. - * - * :param template: PKCS#11 template for attribute matching - * :param objects: found objects, NULL if no objects fit criteria - * :param objects_count: number of objects in objects array - * :return: 1 if success, otherwise return 0 and set the exception - */ -int _find_key(P11_Helper *self, CK_ATTRIBUTE_PTR template, - CK_ULONG template_len, CK_OBJECT_HANDLE **objects, - unsigned int *objects_count) { - CK_OBJECT_HANDLE result_object; - CK_ULONG objectCount; - CK_OBJECT_HANDLE *result_objects = NULL; - CK_OBJECT_HANDLE *tmp_objects_ptr = NULL; - unsigned int count = 0; - unsigned int allocated = 0; - CK_RV rv; - - rv = self->p11->C_FindObjectsInit(self->session, template, template_len); - if (!check_return_value(rv, "Find key init")) - return 0; - - rv = self->p11->C_FindObjects(self->session, &result_object, 1, - &objectCount); - if (!check_return_value(rv, "Find key")) - return 0; - - while (objectCount > 0) { - if (allocated <= count) { - allocated += 32; - tmp_objects_ptr = (CK_OBJECT_HANDLE*) realloc(result_objects, - allocated * sizeof(CK_OBJECT_HANDLE)); - if (tmp_objects_ptr == NULL) { - *objects_count = 0; - PyErr_SetString(ipap11helperError, "_find_key realloc failed"); - free(result_objects); - return 0; - } else { - result_objects = tmp_objects_ptr; - } - } - result_objects[count] = result_object; - count++; - rv = self->p11->C_FindObjects(self->session, &result_object, 1, - &objectCount); - if (!check_return_value(rv, "Check for duplicated key")) { - free(result_objects); - return 0; - } - } - - rv = self->p11->C_FindObjectsFinal(self->session); - if (!check_return_value(rv, "Find objects final")) { - free(result_objects); - return 0; - } - - *objects = result_objects; - *objects_count = count; - return 1; -} - -/* - * Test if object with specified label, id and class exists - * - * :param id: key ID, (if value is NULL, will not be used to find key) - * :param id_len: key ID length - * :param label key: label (if value is NULL, will not be used to find key) - * :param label_len: key label length - * :param class key: class - - * :return: 1 if object was found, 0 if object doesnt exists, -1 if error - * and set the exception - * - */ -int _id_exists(P11_Helper *self, CK_BYTE_PTR id, CK_ULONG id_len, - CK_OBJECT_CLASS class) { - - CK_RV rv; - CK_ULONG object_count = 0; - CK_OBJECT_HANDLE result_object = 0; - CK_OBJECT_CLASS class_sec = CKO_SECRET_KEY; - - CK_ATTRIBUTE template_pub_priv[] = { - { CKA_ID, id, id_len }, - { CKA_CLASS, &class, sizeof(CK_OBJECT_CLASS) } - }; - - CK_ATTRIBUTE template_sec[] = { - { CKA_ID, id, id_len }, - { CKA_CLASS, &class_sec, sizeof(CK_OBJECT_CLASS) } - }; - - CK_ATTRIBUTE template_id[] = { - { CKA_ID, id, id_len } - }; - - /* - * Only one secret key with same ID is allowed - */ - if (class == CKO_SECRET_KEY) { - rv = self->p11->C_FindObjectsInit(self->session, template_id, 1); - if (!check_return_value(rv, "id, label exists init")) - return -1; - - rv = self->p11->C_FindObjects(self->session, &result_object, 1, - &object_count); - if (!check_return_value(rv, "id, label exists")) - return -1; - - rv = self->p11->C_FindObjectsFinal(self->session); - if (!check_return_value(rv, "id, label exists final")) - return -1; - - if (object_count > 0) { - /* object found */ - return 1; - } - return 0; - } - - /* - * Public and private keys can share one ID, but - */ - - /* test if secret key with same ID exists */ - rv = self->p11->C_FindObjectsInit(self->session, template_sec, 2); - if (!check_return_value(rv, "id, label exists init")) - return -1; - - rv = self->p11->C_FindObjects(self->session, &result_object, 1, - &object_count); - if (!check_return_value(rv, "id, label exists")) - return -1; - - rv = self->p11->C_FindObjectsFinal(self->session); - if (!check_return_value(rv, "id, label exists final")) - return -1; - - if (object_count > 0) { - /* object found */ - return 1; - } - - /* test if pub/private key with same id exists */ - object_count = 0; - - rv = self->p11->C_FindObjectsInit(self->session, template_pub_priv, 2); - if (!check_return_value(rv, "id, label exists init")) - return -1; - - rv = self->p11->C_FindObjects(self->session, &result_object, 1, - &object_count); - if (!check_return_value(rv, "id, label exists")) - return -1; - - rv = self->p11->C_FindObjectsFinal(self->session); - if (!check_return_value(rv, "id, label exists final")) - return -1; - - if (object_count > 0) { - return 1; /* Object found*/ - } - - return 0; /* Object not found*/ -} - -/* - * Function set default param values for wrapping mechanism - * :param mech_type: mechanism type - * :param mech: filled structure with params based on mech type - * - * :return: 1 if sucessfull, 0 if error (fill proper exception) - * - * Warning: do not dealloc param values, it is static variables - */ -int _set_wrapping_mech_parameters(CK_MECHANISM_TYPE mech_type, - CK_MECHANISM *mech) { - switch (mech_type) { - case CKM_RSA_PKCS: - case CKM_AES_KEY_WRAP: - case CKM_AES_KEY_WRAP_PAD: - mech->pParameter = NULL; - mech->ulParameterLen = 0; - break; - - case CKM_RSA_PKCS_OAEP: - /* Use the same configuration as openSSL - * https://www.openssl.org/docs/crypto/RSA_public_encrypt.html - */ - mech->pParameter = (void *) &CONST_RSA_PKCS_OAEP_PARAMS; - mech->ulParameterLen = sizeof(CONST_RSA_PKCS_OAEP_PARAMS); - break; - - default: - PyErr_SetString(ipap11helperError, - "Unsupported wrapping mechanism"); - return 0; - } - mech->mechanism = mech_type; - return 1; -} - - -/*********************************************************************** - * P11_Helper object - */ - -static void P11_Helper_dealloc(P11_Helper *self) { - Py_TYPE(self)->tp_free((PyObject *) self); -} - -static PyObject *P11_Helper_new(PyTypeObject *type, PyObject *args, - PyObject *kwds) { - P11_Helper *self; - - self = (P11_Helper *) type->tp_alloc(type, 0); - if (self != NULL) { - - self->slot = 0; - self->session = 0; - self->p11 = NULL; - self->module_handle = NULL; - } - - return (PyObject *) self; -} - -static int P11_Helper_init(P11_Helper *self, PyObject *args, PyObject *kwds) { - const char *user_pin = NULL; - const char *library_path = NULL; - CK_RV rv; - void *module_handle = NULL; - - /* Parse method args */ - if (!PyArg_ParseTuple(args, "iss", &self->slot, &user_pin, &library_path)) - return -1; - - CK_C_GetFunctionList pGetFunctionList = loadLibrary(library_path, - &module_handle); - if (!pGetFunctionList) { - PyErr_SetString(ipap11helperError, "Could not load the library."); - return -1; - } - - self->module_handle = module_handle; - - /* - * Load the function list - */ - (*pGetFunctionList)(&self->p11); - - /* - * Initialize - */ - rv = self->p11->C_Initialize(NULL); - if (!check_return_value(rv, "initialize")) - return -1; - - /* - *Start session - */ - rv = self->p11->C_OpenSession(self->slot, - CKF_SERIAL_SESSION | CKF_RW_SESSION, NULL, - NULL, &self->session); - if (!check_return_value(rv, "open session")) - return -1; - - /* - * Login - */ - rv = self->p11->C_Login(self->session, CKU_USER, (CK_BYTE *) user_pin, - strlen((char *) user_pin)); - if (!check_return_value(rv, "log in")) - return -1; - - return 0; -} - -static PyMemberDef P11_Helper_members[] = { - { NULL } /* Sentinel */ -}; - -/* - * Finalize operations with pkcs11 library - */ -static PyObject *P11_Helper_finalize(P11_Helper *self) { - CK_RV rv; - - if (self->p11 == NULL) - Py_RETURN_NONE; - - /* - * Logout - */ - rv = self->p11->C_Logout(self->session); - if (rv != CKR_USER_NOT_LOGGED_IN) { - if (!check_return_value(rv, "log out")) - return NULL; - } - - /* - * End session - */ - rv = self->p11->C_CloseSession(self->session); - if (!check_return_value(rv, "close session")) - return NULL; - - /* - * Finalize - */ - self->p11->C_Finalize(NULL); - - unloadLibrary(self->module_handle); - - self->p11 = NULL; - self->session = 0; - self->slot = 0; - self->module_handle = NULL; - - Py_RETURN_NONE; -} - -/******************************************************************** - * Methods working with keys - */ - -/** - * Generate master key - * - *:return: master key handle - */ -static PyObject *P11_Helper_generate_master_key(P11_Helper *self, - PyObject *args, - PyObject *kwds) { - PyObj2Bool_mapping_t attrs[] = { - { NULL, &true }, // sec_en_cka_copyable - { NULL, &false }, // sec_en_cka_decrypt - { NULL, &false }, // sec_en_cka_derive - { NULL, &false }, // sec_en_cka_encrypt - { NULL, &true }, // sec_en_cka_extractable - { NULL, &true }, // sec_en_cka_modifiable - { NULL, &true }, // sec_en_cka_private - { NULL, &true }, // sec_en_cka_sensitive - { NULL, &false }, // sec_en_cka_sign - { NULL, &true }, // sec_en_cka_unwrap - { NULL, &false }, // sec_en_cka_verify - { NULL, &true }, // sec_en_cka_wrap - { NULL, &false } // sec_en_cka_wrap_with_trusted - }; - - CK_ULONG key_length = 16; - CK_RV rv; - CK_OBJECT_HANDLE master_key; - CK_BYTE *id = NULL; - int id_length = 0; - - PyObject *label_unicode = NULL; - Py_ssize_t label_length = 0; - CK_BYTE *label = NULL; - int r; - int error = 0; - static char *kwlist[] = { "subject", "id", "key_length", "cka_copyable", - "cka_decrypt", "cka_derive", "cka_encrypt", "cka_extractable", - "cka_modifiable", "cka_private", "cka_sensitive", "cka_sign", - "cka_unwrap", "cka_verify", "cka_wrap", "cka_wrap_with_trusted", - NULL - }; - //TODO check long overflow - if (!PyArg_ParseTupleAndKeywords(args, kwds, "Us#|kOOOOOOOOOOOOO", kwlist, - &label_unicode, &id, &id_length, - &key_length, - &attrs[sec_en_cka_copyable].py_obj, - &attrs[sec_en_cka_decrypt].py_obj, - &attrs[sec_en_cka_derive].py_obj, - &attrs[sec_en_cka_encrypt].py_obj, - &attrs[sec_en_cka_extractable].py_obj, - &attrs[sec_en_cka_modifiable].py_obj, - &attrs[sec_en_cka_private].py_obj, - &attrs[sec_en_cka_sensitive].py_obj, - &attrs[sec_en_cka_sign].py_obj, - &attrs[sec_en_cka_unwrap].py_obj, - &attrs[sec_en_cka_verify].py_obj, - &attrs[sec_en_cka_wrap].py_obj, - &attrs - [sec_en_cka_wrap_with_trusted].py_obj)) { - return NULL; - } - - label = (unsigned char *) unicode_to_char_array(label_unicode, - &label_length); - if (label == NULL) - GOTO_FAIL; - - CK_MECHANISM mechanism = { //TODO param? - CKM_AES_KEY_GEN, NULL_PTR, 0 - }; - - if ((key_length != 16) && (key_length != 24) && (key_length != 32)) { - PyErr_SetString(ipap11helperError, - "generate_master_key: key length allowed values are: 16, 24 and 32"); - GOTO_FAIL; - } - - r = _id_exists(self, id, id_length, CKO_SECRET_KEY); - if (r == 1) { - PyErr_SetString(ipap11helperDuplicationError, - "Master key with same ID already exists"); - GOTO_FAIL; - } else if (r == -1) { - GOTO_FAIL; - } - - /* Process keyword boolean arguments */ - convert_py2bool(attrs, sizeof(attrs) / sizeof(PyObj2Bool_mapping_t)); - - CK_ATTRIBUTE symKeyTemplate[] = { - { CKA_ID, id, id_length }, - { CKA_LABEL, label, label_length }, - { CKA_TOKEN, &true, sizeof(CK_BBOOL) }, - { CKA_VALUE_LEN, &key_length, sizeof(key_length) }, - //{ CKA_COPYABLE, attrs[sec_en_cka_copyable].bool, sizeof(CK_BBOOL) }, //TODO Softhsm doesn't support it - { CKA_DECRYPT, attrs[sec_en_cka_decrypt].bool, sizeof(CK_BBOOL) }, - { CKA_DERIVE, attrs[sec_en_cka_derive].bool, sizeof(CK_BBOOL) }, - { CKA_ENCRYPT, attrs[sec_en_cka_encrypt].bool, sizeof(CK_BBOOL) }, - { CKA_EXTRACTABLE, attrs[sec_en_cka_extractable].bool, sizeof(CK_BBOOL) }, - { CKA_MODIFIABLE, attrs[sec_en_cka_modifiable].bool, sizeof(CK_BBOOL) }, - { CKA_PRIVATE, attrs[sec_en_cka_private].bool, sizeof(CK_BBOOL) }, - { CKA_SENSITIVE, attrs[sec_en_cka_sensitive].bool, sizeof(CK_BBOOL) }, - { CKA_SIGN, attrs[sec_en_cka_sign].bool, sizeof(CK_BBOOL) }, - { CKA_UNWRAP, attrs[sec_en_cka_unwrap].bool, sizeof(CK_BBOOL) }, - { CKA_VERIFY, attrs[sec_en_cka_verify].bool, sizeof(CK_BBOOL) }, - { CKA_WRAP, attrs[sec_en_cka_wrap].bool, sizeof(CK_BBOOL) }, - { CKA_WRAP_WITH_TRUSTED, attrs[sec_en_cka_wrap_with_trusted].bool, sizeof(CK_BBOOL) } - }; - - rv = self->p11->C_GenerateKey(self->session, &mechanism, symKeyTemplate, - sizeof(symKeyTemplate) / - sizeof(CK_ATTRIBUTE), &master_key); - if (!check_return_value(rv, "generate master key")) { - GOTO_FAIL; - } -final: - if (label != NULL) - PyMem_Free(label); - - if (error) - return NULL; - return Py_BuildValue("k", master_key); -} - -/** - * Generate replica keys - * - * :returns: tuple (public_key_handle, private_key_handle) - */ -static PyObject *P11_Helper_generate_replica_key_pair(P11_Helper *self, - PyObject *args, - PyObject *kwds) { - CK_RV rv; - int r; - CK_ULONG modulus_bits = 2048; - CK_BYTE *id = NULL; - int id_length = 0; - PyObject *label_unicode = NULL; - Py_ssize_t label_length = 0; - CK_BYTE *label = NULL; - int error = 0; - - PyObj2Bool_mapping_t attrs_pub[] = { - { NULL, &true }, // pub_en_cka_copyable - { NULL, &false }, // pub_en_cka_derive - { NULL, &false }, // pub_en_cka_encrypt - { NULL, &true }, // pub_en_cka_modifiable - { NULL, &true }, // pub_en_cka_private - { NULL, &false }, // pub_en_cka_trusted - { NULL, &false }, // pub_en_cka_verify - { NULL, &false }, // pub_en_cka_verify_recover - { NULL, &true }, // pub_en_cka_wrap - }; - - PyObj2Bool_mapping_t attrs_priv[] = { - { NULL, &false }, // priv_en_cka_always_authenticate - { NULL, &true }, // priv_en_cka_copyable - { NULL, &false }, // priv_en_cka_decrypt - { NULL, &false }, // priv_en_cka_derive - { NULL, &false }, // priv_en_cka_extractable - { NULL, &true }, // priv_en_cka_modifiable - { NULL, &true }, // priv_en_cka_private - { NULL, &true }, // priv_en_cka_sensitive - { NULL, &false }, // priv_en_cka_sign - { NULL, &false }, // priv_en_cka_sign_recover - { NULL, &true }, // priv_en_cka_unwrap - { NULL, &false } // priv_en_cka_wrap_with_trusted - }; - - static char *kwlist[] = { "label", "id", "modulus_bits", - /* public key kw */ - "pub_cka_copyable", "pub_cka_derive", "pub_cka_encrypt", - "pub_cka_modifiable", "pub_cka_private", "pub_cka_trusted", - "pub_cka_verify", "pub_cka_verify_recover", "pub_cka_wrap", - /* private key kw */ - "priv_cka_always_authenticate", "priv_cka_copyable", - "priv_cka_decrypt", "priv_cka_derive", "priv_cka_extractable", - "priv_cka_modifiable", "priv_cka_private", "priv_cka_sensitive", - "priv_cka_sign", "priv_cka_sign_recover", "priv_cka_unwrap", - "priv_cka_wrap_with_trusted", NULL - }; - - if (!PyArg_ParseTupleAndKeywords(args, kwds, "Us#|kOOOOOOOOOOOOOOOOOOOOO", - kwlist, &label_unicode, &id, &id_length, - &modulus_bits, - /* public key kw */ - &attrs_pub[pub_en_cka_copyable].py_obj, - &attrs_pub[pub_en_cka_derive].py_obj, - &attrs_pub[pub_en_cka_encrypt].py_obj, - &attrs_pub[pub_en_cka_modifiable].py_obj, - &attrs_pub[pub_en_cka_private].py_obj, - &attrs_pub[pub_en_cka_trusted].py_obj, - &attrs_pub[pub_en_cka_verify].py_obj, - &attrs_pub[pub_en_cka_verify_recover].py_obj, - &attrs_pub[pub_en_cka_wrap].py_obj, - /* private key kw */ - &attrs_priv[priv_en_cka_always_authenticate].py_obj, - &attrs_priv[priv_en_cka_copyable].py_obj, - &attrs_priv[priv_en_cka_decrypt].py_obj, - &attrs_priv[priv_en_cka_derive].py_obj, - &attrs_priv[priv_en_cka_extractable].py_obj, - &attrs_priv[priv_en_cka_modifiable].py_obj, - &attrs_priv[priv_en_cka_private].py_obj, - &attrs_priv[priv_en_cka_sensitive].py_obj, - &attrs_priv[priv_en_cka_sign].py_obj, - &attrs_priv[priv_en_cka_sign_recover].py_obj, - &attrs_priv[priv_en_cka_unwrap].py_obj, - &attrs_priv[priv_en_cka_wrap_with_trusted].py_obj)) { - return NULL; - } - - label = unicode_to_char_array(label_unicode, &label_length); - if (label == NULL) - GOTO_FAIL; - - CK_OBJECT_HANDLE public_key, private_key; - CK_MECHANISM mechanism = { CKM_RSA_PKCS_KEY_PAIR_GEN, NULL_PTR, 0 }; - - r = _id_exists(self, id, id_length, CKO_PRIVATE_KEY); - if (r == 1) { - PyErr_SetString(ipap11helperDuplicationError, - "Private key with same ID already exists"); - GOTO_FAIL; - } else if (r == -1) - GOTO_FAIL; - - r = _id_exists(self, id, id_length, CKO_PUBLIC_KEY); - if (r == 1) { - PyErr_SetString(ipap11helperDuplicationError, - "Public key with same ID already exists"); - GOTO_FAIL; - } else if (r == -1) - GOTO_FAIL; - - /* Process keyword boolean arguments */ - convert_py2bool(attrs_pub, - sizeof(attrs_pub) / sizeof(PyObj2Bool_mapping_t)); - convert_py2bool(attrs_priv, - sizeof(attrs_priv) / sizeof(PyObj2Bool_mapping_t)); - - CK_BYTE public_exponent[] = { 1, 0, 1 }; /* 65537 (RFC 6376 section 3.3.1) */ - CK_ATTRIBUTE publicKeyTemplate[] = { - { CKA_ID, id, id_length }, - { CKA_LABEL, label, label_length }, - { CKA_TOKEN, &true, sizeof(true) }, - { CKA_MODULUS_BITS, &modulus_bits, sizeof(modulus_bits) }, - { CKA_PUBLIC_EXPONENT, public_exponent, 3 }, - //{ CKA_COPYABLE, attrs_pub[pub_en_cka_copyable].bool, sizeof(CK_BBOOL) }, //TODO Softhsm doesn't support it - { CKA_DERIVE, attrs_pub[pub_en_cka_derive].bool, sizeof(CK_BBOOL) }, - { CKA_ENCRYPT, attrs_pub[pub_en_cka_encrypt].bool, sizeof(CK_BBOOL) }, - { CKA_MODIFIABLE, attrs_pub[pub_en_cka_modifiable].bool, sizeof(CK_BBOOL) }, - { CKA_PRIVATE, attrs_pub[pub_en_cka_private].bool, sizeof(CK_BBOOL) }, - { CKA_TRUSTED, attrs_pub[pub_en_cka_trusted].bool, sizeof(CK_BBOOL) }, - { CKA_VERIFY, attrs_pub[pub_en_cka_verify].bool, sizeof(CK_BBOOL) }, - { CKA_VERIFY_RECOVER, attrs_pub[pub_en_cka_verify_recover].bool, sizeof(CK_BBOOL) }, - { CKA_WRAP, attrs_pub[pub_en_cka_wrap].bool, sizeof(CK_BBOOL) }, }; - - CK_ATTRIBUTE privateKeyTemplate[] = { - { CKA_ID, id, id_length }, - { CKA_LABEL, label, label_length }, - { CKA_TOKEN, &true, sizeof(true) }, - { CKA_ALWAYS_AUTHENTICATE, attrs_priv[priv_en_cka_always_authenticate].bool, sizeof(CK_BBOOL) }, - //{ CKA_COPYABLE, attrs_priv[priv_en_cka_copyable].bool, sizeof(CK_BBOOL) }, //TODO Softhsm doesn't support it - { CKA_DECRYPT, attrs_priv[priv_en_cka_decrypt].bool, sizeof(CK_BBOOL) }, - { CKA_DERIVE, attrs_priv[priv_en_cka_derive].bool, sizeof(CK_BBOOL) }, - { CKA_EXTRACTABLE, attrs_priv[priv_en_cka_extractable].bool, sizeof(CK_BBOOL) }, - { CKA_MODIFIABLE, attrs_priv[priv_en_cka_modifiable].bool, sizeof(CK_BBOOL) }, - { CKA_PRIVATE, attrs_priv[priv_en_cka_private].bool, sizeof(CK_BBOOL) }, - { CKA_SENSITIVE, attrs_priv[priv_en_cka_sensitive].bool, sizeof(CK_BBOOL) }, - { CKA_SIGN, attrs_priv[priv_en_cka_sign].bool, sizeof(CK_BBOOL) }, - { CKA_SIGN_RECOVER, attrs_priv[priv_en_cka_sign].bool, sizeof(CK_BBOOL) }, - { CKA_UNWRAP, attrs_priv[priv_en_cka_unwrap].bool, sizeof(CK_BBOOL) }, - { CKA_WRAP_WITH_TRUSTED, attrs_priv[priv_en_cka_wrap_with_trusted].bool, sizeof(CK_BBOOL) } - }; - - rv = self->p11->C_GenerateKeyPair(self->session, &mechanism, - publicKeyTemplate, - sizeof(publicKeyTemplate) / sizeof(CK_ATTRIBUTE), - privateKeyTemplate, - sizeof(privateKeyTemplate) / sizeof(CK_ATTRIBUTE), - &public_key, - &private_key); - if (!check_return_value(rv, "generate key pair")) - GOTO_FAIL; - -final: - if (label != NULL) - PyMem_Free(label); - - if (error) - return NULL; - return Py_BuildValue("(kk)", public_key, private_key); -} - -/** - * Find key - */ -static PyObject *P11_Helper_find_keys(P11_Helper *self, PyObject *args, - PyObject *kwds) { - CK_OBJECT_CLASS class = CKO_VENDOR_DEFINED; - CK_OBJECT_CLASS *class_ptr = &class; - CK_BYTE *id = NULL; - CK_BBOOL *ckawrap = NULL; - CK_BBOOL *ckaunwrap = NULL; - int id_length = 0; - PyObject *label_unicode = NULL; - PyObject *cka_wrap_bool = NULL; - PyObject *cka_unwrap_bool = NULL; - Py_ssize_t label_length = 0; - CK_OBJECT_HANDLE *objects = NULL; - unsigned int objects_len = 0; - PyObject *result_list = NULL; - const char *uri_str = NULL; - P11KitUri *uri = NULL; - CK_BYTE *label = NULL; - CK_ATTRIBUTE template_static[MAX_TEMPLATE_LEN]; - CK_ATTRIBUTE_PTR template = template_static; - CK_ULONG template_len = MAX_TEMPLATE_LEN; - int error = 0; - - static char *kwlist[] = { "objclass", "label", "id", "cka_wrap", - "cka_unwrap", "uri", NULL - }; - //TODO check long overflow - if (!PyArg_ParseTupleAndKeywords(args, kwds, "|iUz#OOs", kwlist, &class, - &label_unicode, &id, &id_length, - &cka_wrap_bool, &cka_unwrap_bool, - &uri_str)) { - GOTO_FAIL; - } - - if (label_unicode != NULL) { - label = (unsigned char *) unicode_to_char_array(label_unicode, &label_length); //TODO verify signed/unsigned - if (label == NULL) - GOTO_FAIL; - } - - if (cka_wrap_bool != NULL) { - if (PyObject_IsTrue(cka_wrap_bool)) { - ckawrap = &true; - } else { - ckawrap = &false; - } - } - - if (cka_unwrap_bool != NULL) { - if (PyObject_IsTrue(cka_unwrap_bool)) { - ckaunwrap = &true; - } else { - ckaunwrap = &false; - } - } - - if (class == CKO_VENDOR_DEFINED) - class_ptr = NULL; - - if (uri_str == NULL) - _fill_template_from_parts(template, &template_len, id, id_length, - label, label_length, class_ptr, ckawrap, - ckaunwrap); - else { - if (!_parse_uri(uri_str, &uri)) { - GOTO_FAIL; - } - template = p11_kit_uri_get_attributes(uri, &template_len); - /* Do not deallocate URI while you are using the template. - * Template contains pointers to values inside URI! */ - } - - if (!_find_key(self, template, template_len, &objects, &objects_len)) { - GOTO_FAIL; - } - - result_list = PyList_New(objects_len); - if (result_list == NULL) { - PyErr_SetString(ipap11helperError, - "Unable to create list with results"); - GOTO_FAIL; - } - - for (int i = 0; i < objects_len; ++i) { - if (PyList_SetItem(result_list, i, Py_BuildValue("k", objects[i])) - == -1) { - PyErr_SetString(ipap11helperError, - "Unable to add to value to result list"); - Py_DECREF(result_list); - GOTO_FAIL; - } - } -final: - if (label != NULL) - PyMem_Free(label); - if (objects != NULL) - free(objects); - if (uri != NULL) - p11_kit_uri_free(uri); - - if (error) - return NULL; - return result_list; -} - -/** - * delete key - */ -static PyObject *P11_Helper_delete_key(P11_Helper *self, PyObject *args, - PyObject *kwds) { - CK_RV rv; - CK_OBJECT_HANDLE key_handle = 0; - static char *kwlist[] = { "key_handle", NULL }; - //TODO check long overflow - if (!PyArg_ParseTupleAndKeywords(args, kwds, "k|", kwlist, &key_handle)) { - return NULL; - } - rv = self->p11->C_DestroyObject(self->session, key_handle); - if (!check_return_value(rv, "object deletion")) { - return NULL; - } - - Py_RETURN_NONE; -} - -/** - * export RSA public key - */ -static PyObject *P11_Helper_export_RSA_public_key(P11_Helper *self, - CK_OBJECT_HANDLE object) { - CK_RV rv; - PyObject *ret = NULL; - - int pp_len; - unsigned char *pp = NULL; - EVP_PKEY *pkey = NULL; - BIGNUM *e = NULL; - BIGNUM *n = NULL; - RSA *rsa = NULL; - CK_BYTE_PTR modulus = NULL; - CK_BYTE_PTR exponent = NULL; - CK_OBJECT_CLASS class = CKO_PUBLIC_KEY; - CK_KEY_TYPE key_type = CKK_RSA; - int error = 0; - - CK_ATTRIBUTE obj_template[] = { - { CKA_MODULUS, NULL_PTR, 0 }, - { CKA_PUBLIC_EXPONENT, NULL_PTR, 0 }, - { CKA_CLASS, &class, sizeof(class) }, - { CKA_KEY_TYPE, &key_type, sizeof(key_type) } - }; - - rv = self->p11->C_GetAttributeValue(self->session, object, obj_template, - sizeof(obj_template) / sizeof(CK_ATTRIBUTE)); - if (!check_return_value(rv, "get RSA public key values - prepare")) - GOTO_FAIL; - - /* Set proper size for attributes */ - modulus = - (CK_BYTE_PTR) PyMem_Malloc(obj_template[0].ulValueLen * - sizeof(CK_BYTE)); - if (modulus == NULL) { - PyErr_NoMemory(); - GOTO_FAIL; - } - obj_template[0].pValue = modulus; - exponent = - (CK_BYTE_PTR) PyMem_Malloc(obj_template[1].ulValueLen * - sizeof(CK_BYTE)); - if (exponent == NULL) { - PyErr_NoMemory(); - GOTO_FAIL; - } - obj_template[1].pValue = exponent; - - rv = self->p11->C_GetAttributeValue(self->session, object, obj_template, - sizeof(obj_template) / sizeof(CK_ATTRIBUTE)); - if (!check_return_value(rv, "get RSA public key values")) { - GOTO_FAIL; - } - - /* Check if the key is RSA public key */ - if (class != CKO_PUBLIC_KEY) { - PyErr_SetString(ipap11helperError, - "export_RSA_public_key: required public key class"); - GOTO_FAIL; - } - - if (key_type != CKK_RSA) { - PyErr_SetString(ipap11helperError, - "export_RSA_public_key: required RSA key type"); - GOTO_FAIL; - } - - rsa = RSA_new(); - pkey = EVP_PKEY_new(); - n = BN_bin2bn((const unsigned char *) modulus, - obj_template[0].ulValueLen * sizeof(CK_BYTE), NULL); - if (n == NULL) { - PyErr_SetString(ipap11helperError, - "export_RSA_public_key: internal error: unable to convert modulus"); - GOTO_FAIL; - } - - e = BN_bin2bn((const unsigned char *) exponent, - obj_template[1].ulValueLen * sizeof(CK_BYTE), NULL); - if (e == NULL) { - PyErr_SetString(ipap11helperError, - "export_RSA_public_key: internal error: unable to convert exponent"); - GOTO_FAIL; - } - - /* set modulus and exponent */ - rsa->n = n; - rsa->e = e; - - if (EVP_PKEY_set1_RSA(pkey, rsa) == 0) { - PyErr_SetString(ipap11helperError, - "export_RSA_public_key: internal error: EVP_PKEY_set1_RSA failed"); - GOTO_FAIL; - } - - pp_len = i2d_PUBKEY(pkey, &pp); - ret = string_to_pybytes_or_none(pp, pp_len); - -final: - if (rsa != NULL) { - RSA_free(rsa); // this frees also 'n' and 'e' - } else { - if (n != NULL) - BN_free(n); - if (e != NULL) - BN_free(e); - } - - if (pkey != NULL) - EVP_PKEY_free(pkey); - if (pp != NULL) - free(pp); - if (modulus != NULL) - PyMem_Free(modulus); - if (exponent != NULL) - PyMem_Free(exponent); - - if (error) - return NULL; - return ret; -} - -/** - * Export public key - * - * Export public key in SubjectPublicKeyInfo (RFC5280) DER encoded format - */ -static PyObject *P11_Helper_export_public_key(P11_Helper *self, - PyObject *args, PyObject *kwds) { - CK_RV rv; - CK_OBJECT_HANDLE object = 0; - CK_OBJECT_CLASS class = CKO_PUBLIC_KEY; - CK_KEY_TYPE key_type = CKK_RSA; - static char *kwlist[] = { "key_handle", NULL }; - //TODO check long overflow - if (!PyArg_ParseTupleAndKeywords(args, kwds, "k|", kwlist, &object)) { - return NULL; - } - - CK_ATTRIBUTE obj_template[] = { - { CKA_CLASS, &class, sizeof(class) }, - { CKA_KEY_TYPE, &key_type, sizeof(key_type) } - }; - - rv = self->p11->C_GetAttributeValue(self->session, object, obj_template, - sizeof(obj_template) / sizeof(CK_ATTRIBUTE)); - if (!check_return_value - (rv, "export_public_key: get RSA public key values")) - return NULL; - - if (class != CKO_PUBLIC_KEY) { - PyErr_SetString(ipap11helperError, - "export_public_key: required public key class"); - return NULL; - } - - switch (key_type) { - case CKK_RSA: - return P11_Helper_export_RSA_public_key(self, object); - break; - default: - PyErr_SetString(ipap11helperError, - "export_public_key: unsupported key type"); - } - - return NULL; -} - -/** - * Import RSA public key - * - */ -static PyObject *P11_Helper_import_RSA_public_key(P11_Helper *self, - CK_UTF8CHAR * label, - Py_ssize_t label_length, - CK_BYTE * id, - Py_ssize_t id_length, - EVP_PKEY * pkey, - CK_BBOOL *cka_copyable, - CK_BBOOL *cka_derive, - CK_BBOOL *cka_encrypt, - CK_BBOOL *cka_modifiable, - CK_BBOOL *cka_private, - CK_BBOOL *cka_trusted, - CK_BBOOL *cka_verify, - CK_BBOOL *cka_verify_recover, - CK_BBOOL *cka_wrap) { - CK_RV rv; - CK_OBJECT_CLASS class = CKO_PUBLIC_KEY; - CK_KEY_TYPE keyType = CKK_RSA; - CK_BBOOL *cka_token = &true; - RSA *rsa = NULL; - CK_BYTE_PTR modulus = NULL; - int modulus_len = 0; - CK_BYTE_PTR exponent = NULL; - int exponent_len = 0; - int error = 0; - - if (pkey->type != EVP_PKEY_RSA) { - PyErr_SetString(ipap11helperError, "Required RSA public key"); - GOTO_FAIL; - } - - rsa = EVP_PKEY_get1_RSA(pkey); - if (rsa == NULL) { - PyErr_SetString(ipap11helperError, - "import_RSA_public_key: EVP_PKEY_get1_RSA error"); - GOTO_FAIL; - } - - /* convert BIGNUM to binary array */ - modulus = (CK_BYTE_PTR) PyMem_Malloc(BN_num_bytes(rsa->n)); - if (modulus == NULL) { - PyErr_NoMemory(); - GOTO_FAIL; - } - modulus_len = BN_bn2bin(rsa->n, (unsigned char *) modulus); - if (modulus_len == 0) { - PyErr_SetString(ipap11helperError, - "import_RSA_public_key: BN_bn2bin modulus error"); - GOTO_FAIL; - } - - exponent = (CK_BYTE_PTR) PyMem_Malloc(BN_num_bytes(rsa->e)); - if (exponent == NULL) { - PyErr_NoMemory(); - GOTO_FAIL; - } - exponent_len = BN_bn2bin(rsa->e, (unsigned char *) exponent); - if (exponent_len == 0) { - PyErr_SetString(ipap11helperError, - "import_RSA_public_key: BN_bn2bin exponent error"); - GOTO_FAIL; - } - - CK_ATTRIBUTE template[] = { - { CKA_ID, id, id_length }, - { CKA_CLASS, &class, sizeof(class) }, - { CKA_KEY_TYPE, &keyType, sizeof(keyType) }, - { CKA_TOKEN, cka_token, sizeof(CK_BBOOL) }, - { CKA_LABEL, label, label_length }, - { CKA_MODULUS, modulus, modulus_len }, - { CKA_PUBLIC_EXPONENT, exponent, exponent_len }, - //{ CKA_COPYABLE, cka_copyable, sizeof(CK_BBOOL) }, //TODO Softhsm doesn't support it - { CKA_DERIVE, cka_derive, sizeof(CK_BBOOL) }, - { CKA_ENCRYPT, cka_encrypt, sizeof(CK_BBOOL) }, - { CKA_MODIFIABLE, cka_modifiable, sizeof(CK_BBOOL) }, - { CKA_PRIVATE, cka_private, sizeof(CK_BBOOL) }, - { CKA_TRUSTED, cka_trusted, sizeof(CK_BBOOL) }, - { CKA_VERIFY, cka_verify, sizeof(CK_BBOOL) }, - { CKA_VERIFY_RECOVER, cka_verify_recover, sizeof(CK_BBOOL) }, - { CKA_WRAP, cka_wrap, sizeof(CK_BBOOL) } - }; - CK_OBJECT_HANDLE object; - - rv = self->p11->C_CreateObject(self->session, template, - sizeof(template) / sizeof(CK_ATTRIBUTE), - &object); - if (!check_return_value(rv, "create public key object")) - GOTO_FAIL; - -final: - if (rsa != NULL) - RSA_free(rsa); - if (modulus != NULL) - PyMem_Free(modulus); - if (exponent != NULL) - PyMem_Free(exponent); - - if (error) - return NULL; - return Py_BuildValue("k", object); -} - -/** - * Import RSA public key - * - */ -static PyObject *P11_Helper_import_public_key(P11_Helper *self, - PyObject *args, PyObject *kwds) { - int r; - PyObject *ret = NULL; - PyObject *label_unicode = NULL; - CK_BYTE *id = NULL; - CK_BYTE *data = NULL; - CK_UTF8CHAR *label = NULL; - Py_ssize_t id_length = 0; - Py_ssize_t data_length = 0; - Py_ssize_t label_length = 0; - EVP_PKEY *pkey = NULL; - int error = 0; - - PyObj2Bool_mapping_t attrs_pub[] = { - { NULL, &true }, // pub_en_cka_copyable - { NULL, &false }, // pub_en_cka_derive - { NULL, &false }, // pub_en_cka_encrypt - { NULL, &true }, // pub_en_cka_modifiable - { NULL, &true }, // pub_en_cka_private - { NULL, &false }, // pub_en_cka_trusted - { NULL, &true }, // pub_en_cka_verify - { NULL, &true }, // pub_en_cka_verify_recover - { NULL, &false }, // pub_en_cka_wrap - }; - - static char *kwlist[] = { "label", "id", "data", - /* public key attributes */ - "cka_copyable", "cka_derive", "cka_encrypt", "cka_modifiable", - "cka_private", "cka_trusted", "cka_verify", "cka_verify_recover", - "cka_wrap", NULL - }; - if (!PyArg_ParseTupleAndKeywords(args, kwds, "Us#s#|OOOOOOOOO", kwlist, - &label_unicode, &id, &id_length, &data, - &data_length, - /* public key attributes */ - &attrs_pub[pub_en_cka_copyable].py_obj, - &attrs_pub[pub_en_cka_derive].py_obj, - &attrs_pub[pub_en_cka_encrypt].py_obj, - &attrs_pub[pub_en_cka_modifiable].py_obj, - &attrs_pub[pub_en_cka_private].py_obj, - &attrs_pub[pub_en_cka_trusted].py_obj, - &attrs_pub[pub_en_cka_verify].py_obj, - &attrs_pub[pub_en_cka_verify_recover].py_obj, - &attrs_pub[pub_en_cka_wrap].py_obj)) { - return NULL; - } - - label = (unsigned char *) unicode_to_char_array(label_unicode, - &label_length); - if (label == NULL) - GOTO_FAIL; - - r = _id_exists(self, id, id_length, CKO_PUBLIC_KEY); - if (r == 1) { - PyErr_SetString(ipap11helperDuplicationError, - "Public key with same ID already exists"); - GOTO_FAIL; - } else if (r == -1) { - GOTO_FAIL; - } - - /* Process keyword boolean arguments */ - convert_py2bool(attrs_pub, - sizeof(attrs_pub) / sizeof(PyObj2Bool_mapping_t)); - - /* decode from ASN1 DER */ - pkey = d2i_PUBKEY(NULL, (const unsigned char **) &data, data_length); - if (pkey == NULL) { - PyErr_SetString(ipap11helperError, - "import_public_key: d2i_PUBKEY error"); - GOTO_FAIL; - } - switch (pkey->type) { - case EVP_PKEY_RSA: - ret = P11_Helper_import_RSA_public_key(self, label, label_length, - id, id_length, pkey, - attrs_pub[pub_en_cka_copyable].bool, - attrs_pub[pub_en_cka_derive].bool, - attrs_pub[pub_en_cka_encrypt].bool, - attrs_pub[pub_en_cka_modifiable].bool, - attrs_pub[pub_en_cka_private].bool, - attrs_pub[pub_en_cka_trusted].bool, - attrs_pub[pub_en_cka_verify].bool, - attrs_pub[pub_en_cka_verify_recover].bool, - attrs_pub[pub_en_cka_wrap].bool); - break; - case EVP_PKEY_DSA: - error = 1; - PyErr_SetString(ipap11helperError, "DSA is not supported"); - break; - case EVP_PKEY_EC: - error = 1; - PyErr_SetString(ipap11helperError, "EC is not supported"); - break; - default: - error = 1; - PyErr_SetString(ipap11helperError, "Unsupported key type"); - } -final: - if (pkey != NULL) - EVP_PKEY_free(pkey); - if (label != NULL) - PyMem_Free(label); - - if (error) - return NULL; - return ret; -} - -/** - * Export wrapped key - * - */ -static PyObject *P11_Helper_export_wrapped_key(P11_Helper *self, - PyObject *args, - PyObject *kwds) { - CK_RV rv; - CK_OBJECT_HANDLE object_key = 0; - CK_OBJECT_HANDLE object_wrapping_key = 0; - CK_BYTE_PTR wrapped_key = NULL; - CK_ULONG wrapped_key_len = 0; - CK_MECHANISM wrapping_mech = { CKM_RSA_PKCS, NULL, 0 }; - /* currently we don't support parameter in mechanism */ - PyObject *result = NULL; - int error = 0; - - static char *kwlist[] = { "key", "wrapping_key", "wrapping_mech", NULL }; - //TODO check long overflow - //TODO export method - if (!PyArg_ParseTupleAndKeywords(args, kwds, "kkk|", kwlist, &object_key, - &object_wrapping_key, - &wrapping_mech.mechanism)) - GOTO_FAIL; - - // fill mech parameters - if (!_set_wrapping_mech_parameters(wrapping_mech.mechanism, &wrapping_mech)) - GOTO_FAIL; - - rv = self->p11->C_WrapKey(self->session, &wrapping_mech, - object_wrapping_key, object_key, NULL, - &wrapped_key_len); - if (!check_return_value(rv, "key wrapping: get buffer length")) - GOTO_FAIL; - - wrapped_key = PyMem_Malloc(wrapped_key_len); - if (wrapped_key == NULL) { - PyErr_NoMemory(); - GOTO_FAIL; - } - - rv = self->p11->C_WrapKey(self->session, &wrapping_mech, - object_wrapping_key, object_key, wrapped_key, - &wrapped_key_len); - if (!check_return_value(rv, "key wrapping: wrapping")) - GOTO_FAIL; - - result = string_to_pybytes_or_none(wrapped_key, wrapped_key_len); - -final: - if (wrapped_key != NULL) - PyMem_Free(wrapped_key); - - if (error) - return NULL; - return result; - -} - -/** - * Import wrapped secret key - * - */ -static PyObject *P11_Helper_import_wrapped_secret_key(P11_Helper *self, - PyObject *args, - PyObject *kwds) { - CK_RV rv; - int r; - CK_BYTE_PTR wrapped_key = NULL; - CK_ULONG wrapped_key_len = 0; - CK_ULONG unwrapping_key_object = 0; - CK_OBJECT_HANDLE unwrapped_key_object = 0; - PyObject *label_unicode = NULL; - CK_BYTE *id = NULL; - CK_UTF8CHAR *label = NULL; - Py_ssize_t id_length = 0; - Py_ssize_t label_length = 0; - CK_MECHANISM wrapping_mech = { CKM_RSA_PKCS, NULL, 0 }; - CK_OBJECT_CLASS key_class = CKO_SECRET_KEY; - CK_KEY_TYPE key_type = CKK_RSA; - int error = 0; - - PyObj2Bool_mapping_t attrs[] = { - { NULL, &true }, // sec_en_cka_copyable - { NULL, &false }, // sec_en_cka_decrypt - { NULL, &false }, // sec_en_cka_derive - { NULL, &false }, // sec_en_cka_encrypt - { NULL, &true }, // sec_en_cka_extractable - { NULL, &true }, // sec_en_cka_modifiable - { NULL, &true }, // sec_en_cka_private - { NULL, &true }, // sec_en_cka_sensitive - { NULL, &false }, // sec_en_cka_sign - { NULL, &true }, // sec_en_cka_unwrap - { NULL, &false }, // sec_en_cka_verify - { NULL, &true }, // sec_en_cka_wrap - { NULL, &false } // sec_en_cka_wrap_with_trusted - }; - - static char *kwlist[] = { "label", "id", "data", "unwrapping_key", - "wrapping_mech", "key_type", - // secret key attrs - "cka_copyable", "cka_decrypt", "cka_derive", "cka_encrypt", - "cka_extractable", "cka_modifiable", "cka_private", "cka_sensitive", - "cka_sign", "cka_unwrap", "cka_verify", "cka_wrap", - "cka_wrap_with_trusted", NULL - }; - if (!PyArg_ParseTupleAndKeywords(args, kwds, "Us#s#kkk|OOOOOOOOOOOOO", - kwlist, &label_unicode, &id, &id_length, - &wrapped_key, &wrapped_key_len, - &unwrapping_key_object, - &wrapping_mech.mechanism, &key_type, - // secret key attrs - &attrs[sec_en_cka_copyable].py_obj, - &attrs[sec_en_cka_decrypt].py_obj, - &attrs[sec_en_cka_derive].py_obj, - &attrs[sec_en_cka_encrypt].py_obj, - &attrs[sec_en_cka_extractable].py_obj, - &attrs[sec_en_cka_modifiable].py_obj, - &attrs[sec_en_cka_private].py_obj, - &attrs[sec_en_cka_sensitive].py_obj, - &attrs[sec_en_cka_sign].py_obj, - &attrs[sec_en_cka_unwrap].py_obj, - &attrs[sec_en_cka_verify].py_obj, - &attrs[sec_en_cka_wrap].py_obj, - &attrs[sec_en_cka_wrap_with_trusted].py_obj)) { - return NULL; - } - - if (!_set_wrapping_mech_parameters(wrapping_mech.mechanism, &wrapping_mech)) - return NULL; - - label = (unsigned char *) unicode_to_char_array(label_unicode, - &label_length); - if (label == NULL) - GOTO_FAIL; - - r = _id_exists(self, id, id_length, key_class); - if (r == 1) { - PyErr_SetString(ipap11helperDuplicationError, - "Secret key with same ID already exists"); - GOTO_FAIL; - - } else if (r == -1) - GOTO_FAIL; - - - /* Process keyword boolean arguments */ - convert_py2bool(attrs, sizeof(attrs) / sizeof(PyObj2Bool_mapping_t)); - - CK_ATTRIBUTE template[] = { - { CKA_CLASS, &key_class, sizeof(key_class) }, - { CKA_KEY_TYPE, &key_type, sizeof(key_type) }, - { CKA_ID, id, id_length }, - { CKA_LABEL, label, label_length }, - { CKA_TOKEN, &true, sizeof(CK_BBOOL) }, - //{ CKA_COPYABLE, attrs[sec_en_cka_copyable].bool, sizeof(CK_BBOOL) }, //TODO Softhsm doesn't support it - { CKA_DECRYPT, attrs[sec_en_cka_decrypt].bool, sizeof(CK_BBOOL) }, - { CKA_DERIVE, attrs[sec_en_cka_derive].bool, sizeof(CK_BBOOL) }, - { CKA_ENCRYPT, attrs[sec_en_cka_encrypt].bool, sizeof(CK_BBOOL) }, - { CKA_EXTRACTABLE, attrs[sec_en_cka_extractable].bool, sizeof(CK_BBOOL) }, - { CKA_MODIFIABLE, attrs[sec_en_cka_modifiable].bool, sizeof(CK_BBOOL) }, - { CKA_PRIVATE, attrs[sec_en_cka_private].bool, sizeof(CK_BBOOL) }, - { CKA_SENSITIVE, attrs[sec_en_cka_sensitive].bool, sizeof(CK_BBOOL) }, - { CKA_SIGN, attrs[sec_en_cka_sign].bool, sizeof(CK_BBOOL) }, - { CKA_UNWRAP, attrs[sec_en_cka_unwrap].bool, sizeof(CK_BBOOL) }, - { CKA_VERIFY, attrs[sec_en_cka_verify].bool, sizeof(CK_BBOOL) }, - { CKA_WRAP, attrs[sec_en_cka_wrap].bool, sizeof(CK_BBOOL) }, - { CKA_WRAP_WITH_TRUSTED, attrs[sec_en_cka_wrap_with_trusted].bool, sizeof(CK_BBOOL) } - }; - - rv = self->p11->C_UnwrapKey(self->session, &wrapping_mech, - unwrapping_key_object, wrapped_key, - wrapped_key_len, template, - sizeof(template) / sizeof(CK_ATTRIBUTE), - &unwrapped_key_object); - if (!check_return_value(rv, "import_wrapped_key: key unwrapping")) - GOTO_FAIL; - -final: - if (label != NULL) - PyMem_Free(label); - - if (error) - return NULL; - - return Py_BuildValue("k", unwrapped_key_object); -} - -/** - * Import wrapped private key - * - */ -static PyObject *P11_Helper_import_wrapped_private_key(P11_Helper *self, - PyObject *args, - PyObject *kwds) { - CK_RV rv; - int r; - CK_BYTE_PTR wrapped_key = NULL; - CK_ULONG wrapped_key_len = 0; - CK_ULONG unwrapping_key_object = 0; - CK_OBJECT_HANDLE unwrapped_key_object = 0; - PyObject *label_unicode = NULL; - CK_BYTE *id = NULL; - CK_UTF8CHAR *label = NULL; - Py_ssize_t id_length = 0; - Py_ssize_t label_length = 0; - CK_MECHANISM wrapping_mech = { CKM_RSA_PKCS, NULL, 0 }; - CK_OBJECT_CLASS key_class = CKO_PRIVATE_KEY; - CK_KEY_TYPE key_type = CKK_RSA; - int error = 0; - - PyObj2Bool_mapping_t attrs_priv[] = { - { NULL, &false }, // priv_en_cka_always_authenticate - { NULL, &true }, // priv_en_cka_copyable - { NULL, &false }, // priv_en_cka_decrypt - { NULL, &false }, // priv_en_cka_derive - { NULL, &true }, // priv_en_cka_extractable - { NULL, &true }, // priv_en_cka_modifiable - { NULL, &true }, // priv_en_cka_private - { NULL, &true }, // priv_en_cka_sensitive - { NULL, &true }, // priv_en_cka_sign - { NULL, &true }, // priv_en_cka_sign_recover - { NULL, &false }, // priv_en_cka_unwrap - { NULL, &false } // priv_en_cka_wrap_with_trusted - }; - - static char *kwlist[] = { "label", "id", "data", "unwrapping_key", - "wrapping_mech", "key_type", - // private key attrs - "cka_always_authenticate", "cka_copyable", "cka_decrypt", - "cka_derive", "cka_extractable", "cka_modifiable", "cka_private", - "cka_sensitive", "cka_sign", "cka_sign_recover", "cka_unwrap", - "cka_wrap_with_trusted", NULL - }; - if (!PyArg_ParseTupleAndKeywords(args, kwds, "Us#s#kkk|OOOOOOOOOOOO", - kwlist, &label_unicode, &id, &id_length, - &wrapped_key, &wrapped_key_len, - &unwrapping_key_object, - &wrapping_mech.mechanism, &key_type, - // private key attrs - &attrs_priv[priv_en_cka_always_authenticate].py_obj, - &attrs_priv[priv_en_cka_copyable].py_obj, - &attrs_priv[priv_en_cka_decrypt].py_obj, - &attrs_priv[priv_en_cka_derive].py_obj, - &attrs_priv[priv_en_cka_extractable].py_obj, - &attrs_priv[priv_en_cka_modifiable].py_obj, - &attrs_priv[priv_en_cka_private].py_obj, - &attrs_priv[priv_en_cka_sensitive].py_obj, - &attrs_priv[priv_en_cka_sign].py_obj, - &attrs_priv[priv_en_cka_sign_recover].py_obj, - &attrs_priv[priv_en_cka_unwrap].py_obj, - &attrs_priv[priv_en_cka_wrap_with_trusted].py_obj)) { - return NULL; - } - - label = (unsigned char *) unicode_to_char_array(label_unicode, - &label_length); - if (label == NULL) - GOTO_FAIL; - - r = _id_exists(self, id, id_length, CKO_SECRET_KEY); - if (r == 1) { - PyErr_SetString(ipap11helperDuplicationError, - "Secret key with same ID already exists"); - GOTO_FAIL; - } else if (r == -1) { - GOTO_FAIL; - } - - /* Process keyword boolean arguments */ - convert_py2bool(attrs_priv, - sizeof(attrs_priv) / sizeof(PyObj2Bool_mapping_t)); - - CK_ATTRIBUTE template[] = { - { CKA_CLASS, &key_class, sizeof(key_class) }, - { CKA_KEY_TYPE, &key_type, sizeof(key_type) }, - { CKA_ID, id, id_length }, - { CKA_LABEL, label, label_length }, - { CKA_TOKEN, &true, sizeof(CK_BBOOL) }, - { CKA_ALWAYS_AUTHENTICATE, attrs_priv[priv_en_cka_always_authenticate].bool, sizeof(CK_BBOOL) }, - //{ CKA_COPYABLE, attrs_priv[priv_en_cka_copyable].bool, sizeof(CK_BBOOL) }, //TODO Softhsm doesn't support it - { CKA_DECRYPT, attrs_priv[priv_en_cka_decrypt].bool, sizeof(CK_BBOOL) }, - { CKA_DERIVE, attrs_priv[priv_en_cka_derive].bool, sizeof(CK_BBOOL) }, - { CKA_EXTRACTABLE, attrs_priv[priv_en_cka_extractable].bool, sizeof(CK_BBOOL) }, - { CKA_MODIFIABLE, attrs_priv[priv_en_cka_modifiable].bool, sizeof(CK_BBOOL) }, - { CKA_PRIVATE, attrs_priv[priv_en_cka_private].bool, sizeof(CK_BBOOL) }, - { CKA_SENSITIVE, attrs_priv[priv_en_cka_sensitive].bool, sizeof(CK_BBOOL) }, - { CKA_SIGN, attrs_priv[priv_en_cka_sign].bool, sizeof(CK_BBOOL) }, - { CKA_SIGN_RECOVER, attrs_priv[priv_en_cka_sign].bool, sizeof(CK_BBOOL) }, - { CKA_UNWRAP, attrs_priv[priv_en_cka_unwrap].bool, sizeof(CK_BBOOL) }, - { CKA_WRAP_WITH_TRUSTED, attrs_priv[priv_en_cka_wrap_with_trusted].bool, sizeof(CK_BBOOL) } - }; - - rv = self->p11->C_UnwrapKey(self->session, &wrapping_mech, - unwrapping_key_object, wrapped_key, - wrapped_key_len, template, - sizeof(template) / sizeof(CK_ATTRIBUTE), - &unwrapped_key_object); - if (!check_return_value(rv, "import_wrapped_key: key unwrapping")) { - GOTO_FAIL; - } -final: - if (label != NULL) - PyMem_Free(label); - - if (error) - return NULL; - return PyLong_FromUnsignedLong(unwrapped_key_object); - -} - -/* - * Set object attributes - */ -static PyObject *P11_Helper_set_attribute(P11_Helper *self, PyObject *args, - PyObject *kwds) { - PyObject *ret = Py_None; - PyObject *value = NULL; - CK_ULONG object = 0; - unsigned long attr = 0; - CK_ATTRIBUTE attribute; - CK_RV rv; - Py_ssize_t len = 0; - CK_UTF8CHAR *label = NULL; - int error = 0; - - static char *kwlist[] = { "key_object", "attr", "value", NULL }; - if (!PyArg_ParseTupleAndKeywords - (args, kwds, "kkO|", kwlist, &object, &attr, &value)) { - return NULL; - } - - attribute.type = attr; - switch (attr) { - case CKA_ALWAYS_AUTHENTICATE: - case CKA_ALWAYS_SENSITIVE: - case CKA_COPYABLE: - case CKA_ENCRYPT: - case CKA_EXTRACTABLE: - case CKA_DECRYPT: - case CKA_DERIVE: - case CKA_LOCAL: - case CKA_MODIFIABLE: - case CKA_NEVER_EXTRACTABLE: - case CKA_PRIVATE: - case CKA_SENSITIVE: - case CKA_SIGN: - case CKA_SIGN_RECOVER: - case CKA_TOKEN: - case CKA_TRUSTED: - case CKA_UNWRAP: - case CKA_VERIFY: - case CKA_VERIFY_RECOVER: - case CKA_WRAP: - case CKA_WRAP_WITH_TRUSTED: - attribute.pValue = PyObject_IsTrue(value) ? &true : &false; - attribute.ulValueLen = sizeof(CK_BBOOL); - break; - case CKA_ID: - if (!PyBytes_Check(value)) { - PyErr_SetString(ipap11helperError, "Bytestring value expected"); - GOTO_FAIL; - } - if (PyBytes_AsStringAndSize(value, (char **) &attribute.pValue, - &len) == -1) { - GOTO_FAIL; - } - attribute.ulValueLen = len; - break; - case CKA_LABEL: - if (!PyUnicode_Check(value)) { - PyErr_SetString(ipap11helperError, "Unicode value expected"); - GOTO_FAIL; - } - label = unicode_to_char_array(value, &len); - /* check for conversion error */ - if (label == NULL) - GOTO_FAIL; - attribute.pValue = label; - attribute.ulValueLen = len; - break; - case CKA_KEY_TYPE: - if (!PyInt_Check(value)) { - PyErr_SetString(ipap11helperError, "Integer value expected"); - GOTO_FAIL; - } - unsigned long lv = PyInt_AsUnsignedLongMask(value); - attribute.pValue = &lv; - attribute.ulValueLen = sizeof(unsigned long); - break; - default: - PyErr_SetString(ipap11helperError, "Unknown attribute"); - GOTO_FAIL; - } - - CK_ATTRIBUTE template[] = { attribute }; - - rv = self->p11->C_SetAttributeValue(self->session, object, template, - sizeof(template) / sizeof(CK_ATTRIBUTE)); - if (!check_return_value(rv, "set_attribute")) - GOTO_FAIL; - -final: - if (label != NULL) - PyMem_Free(label); - Py_XINCREF(ret); - - if (error) - return NULL; - return ret; -} - -/* - * Get object attributes - */ -static PyObject *P11_Helper_get_attribute(P11_Helper *self, PyObject *args, - PyObject *kwds) { - PyObject *ret = NULL; - void *value = NULL; - CK_ULONG object = 0; - unsigned long attr = 0; - CK_ATTRIBUTE attribute; - CK_RV rv; - int error = 0; - - static char *kwlist[] = { "key_object", "attr", NULL }; - if (!PyArg_ParseTupleAndKeywords(args, kwds, "kk|", kwlist, &object, - &attr)) { - return NULL; - } - - attribute.type = attr; - attribute.pValue = NULL_PTR; - attribute.ulValueLen = 0; - CK_ATTRIBUTE template[] = { attribute }; - - rv = self->p11->C_GetAttributeValue(self->session, object, template, - sizeof(template) / sizeof(CK_ATTRIBUTE)); - // attribute doesn't exists - if (rv == CKR_ATTRIBUTE_TYPE_INVALID - || template[0].ulValueLen == (unsigned long) -1) { - PyErr_SetString(ipap11helperNotFound, "attribute does not exist"); - GOTO_FAIL; - } - if (!check_return_value(rv, "get_attribute init")) { - GOTO_FAIL; - } - value = PyMem_Malloc(template[0].ulValueLen); - if (value == NULL) { - PyErr_NoMemory(); - GOTO_FAIL; - } - template[0].pValue = value; - - rv = self->p11->C_GetAttributeValue(self->session, object, template, - sizeof(template) / sizeof(CK_ATTRIBUTE)); - if (!check_return_value(rv, "get_attribute")) { - GOTO_FAIL; - } - - switch (attr) { - case CKA_ALWAYS_AUTHENTICATE: - case CKA_ALWAYS_SENSITIVE: - case CKA_COPYABLE: - case CKA_ENCRYPT: - case CKA_EXTRACTABLE: - case CKA_DECRYPT: - case CKA_DERIVE: - case CKA_LOCAL: - case CKA_MODIFIABLE: - case CKA_NEVER_EXTRACTABLE: - case CKA_PRIVATE: - case CKA_SENSITIVE: - case CKA_SIGN: - case CKA_SIGN_RECOVER: - case CKA_TOKEN: - case CKA_TRUSTED: - case CKA_UNWRAP: - case CKA_VERIFY: - case CKA_VERIFY_RECOVER: - case CKA_WRAP: - case CKA_WRAP_WITH_TRUSTED: - /* booleans */ - ret = PyBool_FromLong(*(CK_BBOOL *) value); - break; - case CKA_LABEL: - /* unicode string */ - ret = char_array_to_unicode(value, template[0].ulValueLen); - break; - case CKA_MODULUS: - case CKA_PUBLIC_EXPONENT: - case CKA_ID: - /* byte arrays */ - ret = string_to_pybytes_or_none(value, template[0].ulValueLen); - break; - case CKA_KEY_TYPE: - /* unsigned long */ - ret = Py_BuildValue("k", *(unsigned long *) value); - break; - default: - PyErr_SetString(ipap11helperError, "Unknown attribute"); - GOTO_FAIL; - } - -final: - if (value != NULL) - PyMem_Free(value); - - if (error) - return NULL; - return ret; -} - -static PyMethodDef P11_Helper_methods[] = { - { - "finalize", - (PyCFunction) P11_Helper_finalize, - METH_NOARGS, - "Finalize operations with pkcs11 library" - }, - { - "generate_master_key", - (PyCFunction) P11_Helper_generate_master_key, - METH_VARARGS | METH_KEYWORDS, - "Generate master key" - }, - { - "generate_replica_key_pair", - (PyCFunction) P11_Helper_generate_replica_key_pair, - METH_VARARGS | METH_KEYWORDS, - "Generate replica key pair" - }, - { - "find_keys", - (PyCFunction) P11_Helper_find_keys, - METH_VARARGS | METH_KEYWORDS, - "Find keys" - }, - { - "delete_key", - (PyCFunction) P11_Helper_delete_key, - METH_VARARGS | METH_KEYWORDS, - "Delete key" - }, - { - "export_public_key", - (PyCFunction) P11_Helper_export_public_key, - METH_VARARGS | METH_KEYWORDS, - "Export public key" - }, - { - "import_public_key", - (PyCFunction) P11_Helper_import_public_key, - METH_VARARGS | METH_KEYWORDS, - "Import public key" - }, - { - "export_wrapped_key", - (PyCFunction) P11_Helper_export_wrapped_key, - METH_VARARGS | METH_KEYWORDS, - "Export wrapped private key" - }, - { - "import_wrapped_secret_key", - (PyCFunction) P11_Helper_import_wrapped_secret_key, - METH_VARARGS | METH_KEYWORDS, - "Import wrapped secret key" - }, - { - "import_wrapped_private_key", - (PyCFunction) P11_Helper_import_wrapped_private_key, - METH_VARARGS | METH_KEYWORDS, - "Import wrapped private key" - }, - { - "set_attribute", - (PyCFunction) P11_Helper_set_attribute, - METH_VARARGS | METH_KEYWORDS, - "Set attribute" - }, - { - "get_attribute", - (PyCFunction) P11_Helper_get_attribute, - METH_VARARGS | METH_KEYWORDS, - "Get attribute" - }, - { - /* Sentinel */ - NULL - } -}; - -static PyTypeObject P11_HelperType = { - PyVarObject_HEAD_INIT(NULL, 0) - "_ipap11helper.P11_Helper", /* tp_name */ - sizeof(P11_Helper), /* tp_basicsize */ - 0, /* tp_itemsize */ - (destructor) P11_Helper_dealloc, /* tp_dealloc */ - 0, /* tp_print */ - 0, /* tp_getattr */ - 0, /* tp_setattr */ - 0, /* tp_compare */ - 0, /* tp_repr */ - 0, /* tp_as_number */ - 0, /* tp_as_sequence */ - 0, /* tp_as_mapping */ - 0, /* tp_hash */ - 0, /* tp_call */ - 0, /* tp_str */ - 0, /* tp_getattro */ - 0, /* tp_setattro */ - 0, /* tp_as_buffer */ - Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /* tp_flags */ - "P11_Helper objects", /* tp_doc */ - 0, /* tp_traverse */ - 0, /* tp_clear */ - 0, /* tp_richcompare */ - 0, /* tp_weaklistoffset */ - 0, /* tp_iter */ - 0, /* tp_iternext */ - P11_Helper_methods, /* tp_methods */ - P11_Helper_members, /* tp_members */ - 0, /* tp_getset */ - 0, /* tp_base */ - 0, /* tp_dict */ - 0, /* tp_descr_get */ - 0, /* tp_descr_set */ - 0, /* tp_dictoffset */ - (initproc) P11_Helper_init, /* tp_init */ - 0, /* tp_alloc */ - P11_Helper_new, /* tp_new */ -}; - -static PyMethodDef module_methods[] = { - { NULL } /* Sentinel */ -}; - -#define MODULE_DOC PyDoc_STR("Example module that creates an extension type.") - -#if PY_MAJOR_VERSION >= 3 -static struct PyModuleDef moduledef = { - PyModuleDef_HEAD_INIT, - .m_name = "_ipap11helper", - .m_doc = MODULE_DOC, - .m_size = -1, - .m_methods = module_methods, -}; -#endif - -PyObject * module_init(void); -PyObject * module_init(void) { - PyObject *m; - - if (PyType_Ready(&P11_HelperType) < 0) - return NULL; - - /* - * Setting up P11_Helper module - */ -#if PY_MAJOR_VERSION >= 3 - m = PyModule_Create(&moduledef); -#else - m = Py_InitModule3("_ipap11helper", module_methods, MODULE_DOC); -#endif - - if (m == NULL) - return NULL; - - /* - * Setting up P11_Helper - */ - Py_INCREF(&P11_HelperType); - PyModule_AddObject(m, "P11_Helper", (PyObject *) &P11_HelperType); - - /* - * Setting up P11_Helper Exceptions - */ - ipap11helperException = PyErr_NewException("_ipap11helper.Exception", NULL, - NULL); - Py_INCREF(ipap11helperException); - PyModule_AddObject(m, "Exception", ipap11helperException); - - ipap11helperError = PyErr_NewException("_ipap11helper.Error", - ipap11helperException, NULL); - Py_INCREF(ipap11helperError); - PyModule_AddObject(m, "Error", ipap11helperError); - - ipap11helperNotFound = PyErr_NewException("_ipap11helper.NotFound", - ipap11helperException, NULL); - Py_INCREF(ipap11helperNotFound); - PyModule_AddObject(m, "NotFound", ipap11helperNotFound); - - ipap11helperDuplicationError = - PyErr_NewException("_ipap11helper.DuplicationError", - ipap11helperException, NULL); - Py_INCREF(ipap11helperDuplicationError); - PyModule_AddObject(m, "DuplicationError", ipap11helperDuplicationError); - - /** - * Setting up module attributes - */ - - /* Key Classes */ - PyModule_AddIntConstant(m, "KEY_CLASS_PUBLIC_KEY", CKO_PUBLIC_KEY); - PyModule_AddIntConstant(m, "KEY_CLASS_PRIVATE_KEY", CKO_PRIVATE_KEY); - PyModule_AddIntConstant(m, "KEY_CLASS_SECRET_KEY", CKO_SECRET_KEY); - - /* Key types */ - PyModule_AddIntConstant(m, "KEY_TYPE_RSA", CKK_RSA); - PyModule_AddIntConstant(m, "KEY_TYPE_AES", CKK_AES); - - /* Wrapping mech type */ - PyModule_AddIntConstant(m, "MECH_RSA_PKCS", CKM_RSA_PKCS); - PyModule_AddIntConstant(m, "MECH_RSA_PKCS_OAEP", CKM_RSA_PKCS_OAEP); - PyModule_AddIntConstant(m, "MECH_AES_KEY_WRAP", CKM_AES_KEY_WRAP); - PyModule_AddIntConstant(m, "MECH_AES_KEY_WRAP_PAD", CKM_AES_KEY_WRAP_PAD); - - /* Key attributes */ - PyModule_AddIntMacro(m, CKA_ALWAYS_AUTHENTICATE); - PyModule_AddIntMacro(m, CKA_ALWAYS_SENSITIVE); - PyModule_AddIntMacro(m, CKA_COPYABLE); - PyModule_AddIntMacro(m, CKA_DECRYPT); - PyModule_AddIntMacro(m, CKA_DERIVE); - PyModule_AddIntMacro(m, CKA_ENCRYPT); - PyModule_AddIntMacro(m, CKA_EXTRACTABLE); - PyModule_AddIntMacro(m, CKA_ID); - PyModule_AddIntMacro(m, CKA_KEY_TYPE); - PyModule_AddIntMacro(m, CKA_LOCAL); - PyModule_AddIntMacro(m, CKA_MODIFIABLE); - PyModule_AddIntMacro(m, CKA_MODULUS); - PyModule_AddIntMacro(m, CKA_NEVER_EXTRACTABLE); - PyModule_AddIntMacro(m, CKA_PRIVATE); - PyModule_AddIntMacro(m, CKA_PUBLIC_EXPONENT); - PyModule_AddIntMacro(m, CKA_SENSITIVE); - PyModule_AddIntMacro(m, CKA_SIGN); - PyModule_AddIntMacro(m, CKA_SIGN_RECOVER); - PyModule_AddIntMacro(m, CKA_TRUSTED); - PyModule_AddIntMacro(m, CKA_VERIFY); - PyModule_AddIntMacro(m, CKA_VERIFY_RECOVER); - PyModule_AddIntMacro(m, CKA_UNWRAP); - PyModule_AddIntMacro(m, CKA_WRAP); - PyModule_AddIntMacro(m, CKA_WRAP_WITH_TRUSTED); - PyModule_AddIntMacro(m, CKA_TOKEN); - PyModule_AddIntMacro(m, CKA_LABEL); - - return m; -} - - -#if PY_MAJOR_VERSION >= 3 -PyMODINIT_FUNC PyInit__ipap11helper(void); -PyMODINIT_FUNC PyInit__ipap11helper(void) -{ - return module_init(); -} -#else -void init_ipap11helper(void); -void init_ipap11helper(void) -{ - module_init(); -} -#endif diff --git a/ipapython/ipap11helper/setup.py b/ipapython/ipap11helper/setup.py deleted file mode 100644 index 9743ac229..000000000 --- a/ipapython/ipap11helper/setup.py +++ /dev/null @@ -1,43 +0,0 @@ -#!/usr/bin/python2 -# -# Copyright (C) 2014 FreeIPA Contributors see COPYING for license -# - -from distutils.core import setup, Extension -from distutils.sysconfig import get_python_inc -import sys -import os - -python_header = os.path.join(get_python_inc(plat_specific=0), 'Python.h') -if not os.path.exists(python_header): - sys.exit("Cannot find Python development packages that provide Python.h") - -module = Extension('_ipap11helper', - define_macros = [], - include_dirs = [], - libraries = ['dl', 'crypto', 'p11-kit'], - library_dirs = [], - extra_compile_args = [ - '-std=c99', - '-I/usr/include/p11-kit-1', - '-ggdb3', - '-O2', - '-W', - '-Wall', - '-Wno-unused-parameter', - '-Wbad-function-cast', - '-Wextra', - ], - sources = ['p11helper.c', 'library.c']) - -setup(name='_ipap11helper', - version='0.1', - description='FreeIPA pkcs11 helper', - author='Martin Basti, Petr Spacek', - author_email='mbasti@redhat.com, pspacek@redhat.com', - license='GPLv2+', - url='http://www.freeipa.org', - long_description=""" - FreeIPA pkcs11 key manipulation utils. -""", - ext_modules = [module]) diff --git a/ipapython/p11helper.py b/ipapython/p11helper.py index 10a60cc02..7916baa07 100644 --- a/ipapython/p11helper.py +++ b/ipapython/p11helper.py @@ -2,19 +2,1877 @@ # Copyright (C) 2014 FreeIPA Contributors see COPYING for license # -import _ipap11helper import random +import ctypes.util + +import six +from cffi import FFI + +if six.PY3: + unicode = str + + +_ffi = FFI() + +_ffi.cdef(''' +/* stdlib.h */ + +void free(void *ptr); + + +/* openssl/ossl_typ.h */ + +typedef ... BIGNUM; + +typedef ... EVP_PKEY; + +typedef struct rsa_st RSA; +typedef ... RSA_METHOD; + +typedef ... ENGINE; + +typedef ... CRYPTO_EX_DATA; + + +/* openssl/x509.h */ + +int i2d_PUBKEY(EVP_PKEY *a, unsigned char **pp); +EVP_PKEY *d2i_PUBKEY(EVP_PKEY **a, const unsigned char **pp, long length); + + +/* openssl/evp.h */ + +int EVP_PKEY_set1_RSA(EVP_PKEY *pkey, struct rsa_st *key); +struct rsa_st *EVP_PKEY_get1_RSA(EVP_PKEY *pkey); + +EVP_PKEY *EVP_PKEY_new(void); +void EVP_PKEY_free(EVP_PKEY *pkey); + + +/* openssl/bn.h */ + +int BN_num_bits(const BIGNUM *a); +BIGNUM *BN_bin2bn(const unsigned char *s, int len, BIGNUM *ret); +int BN_bn2bin(const BIGNUM *a, unsigned char *to); + +void BN_free(BIGNUM *a); + + +/* openssl/rsa.h */ + +struct rsa_st { + /* + * The first parameter is used to pickup errors where this is passed + * instead of aEVP_PKEY, it is set to 0 + */ + int pad; + long version; + const RSA_METHOD *meth; + /* functional reference if 'meth' is ENGINE-provided */ + ENGINE *engine; + BIGNUM *n; + BIGNUM *e; + /* ...; */ +}; + +RSA *RSA_new(void); + +void RSA_free(RSA *r); + + +/* p11-kit/pkcs11.h */ + +typedef unsigned long CK_FLAGS; + +struct _CK_VERSION +{ + unsigned char major; + unsigned char minor; +}; + +typedef unsigned long CK_SLOT_ID; + +typedef unsigned long CK_SESSION_HANDLE; + +typedef unsigned long CK_USER_TYPE; + +typedef unsigned long CK_OBJECT_HANDLE; + +typedef unsigned long CK_OBJECT_CLASS; + +typedef unsigned long CK_KEY_TYPE; + +typedef unsigned long CK_ATTRIBUTE_TYPE; + +struct _CK_ATTRIBUTE +{ + CK_ATTRIBUTE_TYPE type; + void *pValue; + unsigned long ulValueLen; +}; + +typedef unsigned long CK_MECHANISM_TYPE; + +struct _CK_MECHANISM +{ + CK_MECHANISM_TYPE mechanism; + void *pParameter; + unsigned long ulParameterLen; +}; + +typedef unsigned long CK_RV; + +typedef ... *CK_NOTIFY; + +struct _CK_FUNCTION_LIST; + +typedef CK_RV (*CK_C_Initialize) (void *init_args); +typedef CK_RV (*CK_C_Finalize) (void *pReserved); +typedef ... *CK_C_GetInfo; +typedef ... *CK_C_GetFunctionList; +CK_RV C_GetFunctionList (struct _CK_FUNCTION_LIST **function_list); +typedef ... *CK_C_GetSlotList; +typedef ... *CK_C_GetSlotInfo; +typedef ... *CK_C_GetTokenInfo; +typedef ... *CK_C_WaitForSlotEvent; +typedef ... *CK_C_GetMechanismList; +typedef ... *CK_C_GetMechanismInfo; +typedef ... *CK_C_InitToken; +typedef ... *CK_C_InitPIN; +typedef ... *CK_C_SetPIN; +typedef CK_RV (*CK_C_OpenSession) (CK_SLOT_ID slotID, CK_FLAGS flags, + void *application, CK_NOTIFY notify, + CK_SESSION_HANDLE *session); +typedef CK_RV (*CK_C_CloseSession) (CK_SESSION_HANDLE session); +typedef ... *CK_C_CloseAllSessions; +typedef ... *CK_C_GetSessionInfo; +typedef ... *CK_C_GetOperationState; +typedef ... *CK_C_SetOperationState; +typedef CK_RV (*CK_C_Login) (CK_SESSION_HANDLE session, CK_USER_TYPE user_type, + unsigned char *pin, unsigned long pin_len); +typedef CK_RV (*CK_C_Logout) (CK_SESSION_HANDLE session); +typedef CK_RV (*CK_C_CreateObject) (CK_SESSION_HANDLE session, + struct _CK_ATTRIBUTE *templ, + unsigned long count, + CK_OBJECT_HANDLE *object); +typedef ... *CK_C_CopyObject; +typedef CK_RV (*CK_C_DestroyObject) (CK_SESSION_HANDLE session, + CK_OBJECT_HANDLE object); +typedef ... *CK_C_GetObjectSize; +typedef CK_RV (*CK_C_GetAttributeValue) (CK_SESSION_HANDLE session, + CK_OBJECT_HANDLE object, + struct _CK_ATTRIBUTE *templ, + unsigned long count); +typedef CK_RV (*CK_C_SetAttributeValue) (CK_SESSION_HANDLE session, + CK_OBJECT_HANDLE object, + struct _CK_ATTRIBUTE *templ, + unsigned long count); +typedef CK_RV (*CK_C_FindObjectsInit) (CK_SESSION_HANDLE session, + struct _CK_ATTRIBUTE *templ, + unsigned long count); +typedef CK_RV (*CK_C_FindObjects) (CK_SESSION_HANDLE session, + CK_OBJECT_HANDLE *object, + unsigned long max_object_count, + unsigned long *object_count); +typedef CK_RV (*CK_C_FindObjectsFinal) (CK_SESSION_HANDLE session); +typedef ... *CK_C_EncryptInit; +typedef ... *CK_C_Encrypt; +typedef ... *CK_C_EncryptUpdate; +typedef ... *CK_C_EncryptFinal; +typedef ... *CK_C_DecryptInit; +typedef ... *CK_C_Decrypt; +typedef ... *CK_C_DecryptUpdate; +typedef ... *CK_C_DecryptFinal; +typedef ... *CK_C_DigestInit; +typedef ... *CK_C_Digest; +typedef ... *CK_C_DigestUpdate; +typedef ... *CK_C_DigestKey; +typedef ... *CK_C_DigestFinal; +typedef ... *CK_C_SignInit; +typedef ... *CK_C_Sign; +typedef ... *CK_C_SignUpdate; +typedef ... *CK_C_SignFinal; +typedef ... *CK_C_SignRecoverInit; +typedef ... *CK_C_SignRecover; +typedef ... *CK_C_VerifyInit; +typedef ... *CK_C_Verify; +typedef ... *CK_C_VerifyUpdate; +typedef ... *CK_C_VerifyFinal; +typedef ... *CK_C_VerifyRecoverInit; +typedef ... *CK_C_VerifyRecover; +typedef ... *CK_C_DigestEncryptUpdate; +typedef ... *CK_C_DecryptDigestUpdate; +typedef ... *CK_C_SignEncryptUpdate; +typedef ... *CK_C_DecryptVerifyUpdate; +typedef CK_RV (*CK_C_GenerateKey) (CK_SESSION_HANDLE session, + struct _CK_MECHANISM *mechanism, + struct _CK_ATTRIBUTE *templ, + unsigned long count, + CK_OBJECT_HANDLE *key); +typedef CK_RV (*CK_C_GenerateKeyPair) (CK_SESSION_HANDLE session, + struct _CK_MECHANISM *mechanism, + struct _CK_ATTRIBUTE * + public_key_template, + unsigned long + public_key_attribute_count, + struct _CK_ATTRIBUTE * + private_key_template, + unsigned long + private_key_attribute_count, + CK_OBJECT_HANDLE *public_key, + CK_OBJECT_HANDLE *private_key); +typedef CK_RV (*CK_C_WrapKey) (CK_SESSION_HANDLE session, + struct _CK_MECHANISM *mechanism, + CK_OBJECT_HANDLE wrapping_key, + CK_OBJECT_HANDLE key, + unsigned char *wrapped_key, + unsigned long *wrapped_key_len); +typedef CK_RV (*CK_C_UnwrapKey) (CK_SESSION_HANDLE session, + struct _CK_MECHANISM *mechanism, + CK_OBJECT_HANDLE unwrapping_key, + unsigned char *wrapped_key, + unsigned long wrapped_key_len, + struct _CK_ATTRIBUTE *templ, + unsigned long attribute_count, + CK_OBJECT_HANDLE *key); +typedef ... *CK_C_DeriveKey; +typedef ... *CK_C_SeedRandom; +typedef ... *CK_C_GenerateRandom; +typedef ... *CK_C_GetFunctionStatus; +typedef ... *CK_C_CancelFunction; + +struct _CK_FUNCTION_LIST +{ + struct _CK_VERSION version; + CK_C_Initialize C_Initialize; + CK_C_Finalize C_Finalize; + CK_C_GetInfo C_GetInfo; + CK_C_GetFunctionList C_GetFunctionList; + CK_C_GetSlotList C_GetSlotList; + CK_C_GetSlotInfo C_GetSlotInfo; + CK_C_GetTokenInfo C_GetTokenInfo; + CK_C_GetMechanismList C_GetMechanismList; + CK_C_GetMechanismInfo C_GetMechanismInfo; + CK_C_InitToken C_InitToken; + CK_C_InitPIN C_InitPIN; + CK_C_SetPIN C_SetPIN; + CK_C_OpenSession C_OpenSession; + CK_C_CloseSession C_CloseSession; + CK_C_CloseAllSessions C_CloseAllSessions; + CK_C_GetSessionInfo C_GetSessionInfo; + CK_C_GetOperationState C_GetOperationState; + CK_C_SetOperationState C_SetOperationState; + CK_C_Login C_Login; + CK_C_Logout C_Logout; + CK_C_CreateObject C_CreateObject; + CK_C_CopyObject C_CopyObject; + CK_C_DestroyObject C_DestroyObject; + CK_C_GetObjectSize C_GetObjectSize; + CK_C_GetAttributeValue C_GetAttributeValue; + CK_C_SetAttributeValue C_SetAttributeValue; + CK_C_FindObjectsInit C_FindObjectsInit; + CK_C_FindObjects C_FindObjects; + CK_C_FindObjectsFinal C_FindObjectsFinal; + CK_C_EncryptInit C_EncryptInit; + CK_C_Encrypt C_Encrypt; + CK_C_EncryptUpdate C_EncryptUpdate; + CK_C_EncryptFinal C_EncryptFinal; + CK_C_DecryptInit C_DecryptInit; + CK_C_Decrypt C_Decrypt; + CK_C_DecryptUpdate C_DecryptUpdate; + CK_C_DecryptFinal C_DecryptFinal; + CK_C_DigestInit C_DigestInit; + CK_C_Digest C_Digest; + CK_C_DigestUpdate C_DigestUpdate; + CK_C_DigestKey C_DigestKey; + CK_C_DigestFinal C_DigestFinal; + CK_C_SignInit C_SignInit; + CK_C_Sign C_Sign; + CK_C_SignUpdate C_SignUpdate; + CK_C_SignFinal C_SignFinal; + CK_C_SignRecoverInit C_SignRecoverInit; + CK_C_SignRecover C_SignRecover; + CK_C_VerifyInit C_VerifyInit; + CK_C_Verify C_Verify; + CK_C_VerifyUpdate C_VerifyUpdate; + CK_C_VerifyFinal C_VerifyFinal; + CK_C_VerifyRecoverInit C_VerifyRecoverInit; + CK_C_VerifyRecover C_VerifyRecover; + CK_C_DigestEncryptUpdate C_DigestEncryptUpdate; + CK_C_DecryptDigestUpdate C_DecryptDigestUpdate; + CK_C_SignEncryptUpdate C_SignEncryptUpdate; + CK_C_DecryptVerifyUpdate C_DecryptVerifyUpdate; + CK_C_GenerateKey C_GenerateKey; + CK_C_GenerateKeyPair C_GenerateKeyPair; + CK_C_WrapKey C_WrapKey; + CK_C_UnwrapKey C_UnwrapKey; + CK_C_DeriveKey C_DeriveKey; + CK_C_SeedRandom C_SeedRandom; + CK_C_GenerateRandom C_GenerateRandom; + CK_C_GetFunctionStatus C_GetFunctionStatus; + CK_C_CancelFunction C_CancelFunction; + CK_C_WaitForSlotEvent C_WaitForSlotEvent; +}; + +typedef unsigned char CK_BYTE; +typedef unsigned char CK_UTF8CHAR; +typedef unsigned char CK_BBOOL; +typedef unsigned long int CK_ULONG; +typedef CK_BYTE *CK_BYTE_PTR; +typedef CK_ULONG *CK_ULONG_PTR; + +typedef CK_OBJECT_HANDLE *CK_OBJECT_HANDLE_PTR; + +typedef struct _CK_ATTRIBUTE CK_ATTRIBUTE; +typedef struct _CK_ATTRIBUTE *CK_ATTRIBUTE_PTR; + +typedef struct _CK_MECHANISM CK_MECHANISM; + +typedef struct _CK_FUNCTION_LIST *CK_FUNCTION_LIST_PTR; + + +/* p11-kit/uri.h */ + +typedef enum { + DUMMY /* ..., */ +} P11KitUriType; + +typedef ... P11KitUri; + +CK_ATTRIBUTE_PTR p11_kit_uri_get_attributes (P11KitUri *uri, + CK_ULONG *n_attrs); + +int p11_kit_uri_any_unrecognized (P11KitUri *uri); + +P11KitUri* p11_kit_uri_new (void); + +int p11_kit_uri_parse (const char *string, + P11KitUriType uri_type, + P11KitUri *uri); + +void p11_kit_uri_free (P11KitUri *uri); + + +/* p11helper.c */ + +struct ck_rsa_pkcs_oaep_params { + CK_MECHANISM_TYPE hash_alg; + unsigned long mgf; + unsigned long source; + void *source_data; + unsigned long source_data_len; +}; + +typedef struct ck_rsa_pkcs_oaep_params CK_RSA_PKCS_OAEP_PARAMS; +''') + +_libc = _ffi.dlopen(None) +_libcrypto = _ffi.dlopen(ctypes.util.find_library('crypto')) +_libp11_kit = _ffi.dlopen(ctypes.util.find_library('p11-kit')) + + +# utility + +NULL = _ffi.NULL + +unsigned_char = _ffi.typeof('unsigned char') +unsigned_char_ptr = _ffi.typeof('unsigned char *') +unsigned_int = _ffi.typeof('unsigned int') +unsigned_long = _ffi.typeof('unsigned long') + +sizeof = _ffi.sizeof + + +def new_ptr(ctype, *args): + return _ffi.new(_ffi.getctype(ctype, '*'), *args) + + +def new_array(ctype, *args): + return _ffi.new(_ffi.getctype(ctype, '[]'), *args) + + +# stdlib.h + +free = _libc.free + + +# openssl/x509.h + +i2d_PUBKEY = _libcrypto.i2d_PUBKEY +d2i_PUBKEY = _libcrypto.i2d_PUBKEY + + +# openssl/evp.h + +EVP_PKEY_RSA = 6 +EVP_PKEY_DSA = 116 +EVP_PKEY_EC = 408 + +EVP_PKEY_set1_RSA = _libcrypto.EVP_PKEY_set1_RSA +EVP_PKEY_get1_RSA = _libcrypto.EVP_PKEY_get1_RSA +EVP_PKEY_new = _libcrypto.EVP_PKEY_new +EVP_PKEY_free = _libcrypto.EVP_PKEY_free + + +# openssl/bn.h + +def BN_num_bytes(a): + return (_libcrypto.BN_num_bits(a) + 7) // 8 + +BN_bin2bn = _libcrypto.BN_bin2bn +BN_bn2bin = _libcrypto.BN_bn2bin + +BN_free = _libcrypto.BN_free + + +# openssl/rsa.h + +RSA_new = _libcrypto.RSA_new + +RSA_free = _libcrypto.RSA_free + + +# p11-kit/pkcs11.h + +CK_SESSION_HANDLE = _ffi.typeof('CK_SESSION_HANDLE') + +CK_OBJECT_HANDLE = _ffi.typeof('CK_OBJECT_HANDLE') + +CKU_USER = 1 + +CKF_RW_SESSION = 0x2 +CKF_SERIAL_SESSION = 0x4 + +CK_OBJECT_CLASS = _ffi.typeof('CK_OBJECT_CLASS') + +CKO_PUBLIC_KEY = 2 +CKO_PRIVATE_KEY = 3 +CKO_SECRET_KEY = 4 +CKO_VENDOR_DEFINED = 0x80000000 + +CK_KEY_TYPE = _ffi.typeof('CK_KEY_TYPE') + +CKK_RSA = 0 +CKK_AES = 0x1f + +CKA_CLASS = 0 +CKA_TOKEN = 1 +CKA_PRIVATE = 2 +CKA_LABEL = 3 +CKA_TRUSTED = 0x86 +CKA_KEY_TYPE = 0x100 +CKA_ID = 0x102 +CKA_SENSITIVE = 0x103 +CKA_ENCRYPT = 0x104 +CKA_DECRYPT = 0x105 +CKA_WRAP = 0x106 +CKA_UNWRAP = 0x107 +CKA_SIGN = 0x108 +CKA_SIGN_RECOVER = 0x109 +CKA_VERIFY = 0x10a +CKA_VERIFY_RECOVER = 0x10b +CKA_DERIVE = 0x10c +CKA_MODULUS = 0x120 +CKA_MODULUS_BITS = 0x121 +CKA_PUBLIC_EXPONENT = 0x122 +CKA_VALUE_LEN = 0x161 +CKA_EXTRACTABLE = 0x162 +CKA_LOCAL = 0x163 +CKA_NEVER_EXTRACTABLE = 0x164 +CKA_ALWAYS_SENSITIVE = 0x165 +CKA_MODIFIABLE = 0x170 +CKA_ALWAYS_AUTHENTICATE = 0x202 +CKA_WRAP_WITH_TRUSTED = 0x210 + +CKM_RSA_PKCS_KEY_PAIR_GEN = 0 +CKM_RSA_PKCS = 1 +CKM_RSA_PKCS_OAEP = 9 +CKM_SHA_1 = 0x220 +CKM_AES_KEY_GEN = 0x1080 + +CKR_OK = 0 +CKR_ATTRIBUTE_TYPE_INVALID = 0x12 +CKR_USER_NOT_LOGGED_IN = 0x101 + +CK_BYTE = _ffi.typeof('CK_BYTE') +CK_BBOOL = _ffi.typeof('CK_BBOOL') +CK_ULONG = _ffi.typeof('CK_ULONG') +CK_BYTE_PTR = _ffi.typeof('CK_BYTE_PTR') +CK_FALSE = 0 +CK_TRUE = 1 + +CK_OBJECT_HANDLE_PTR = _ffi.typeof('CK_OBJECT_HANDLE_PTR') + +CK_ATTRIBUTE = _ffi.typeof('CK_ATTRIBUTE') + +CK_MECHANISM = _ffi.typeof('CK_MECHANISM') + +CK_FUNCTION_LIST_PTR = _ffi.typeof('CK_FUNCTION_LIST_PTR') + +NULL_PTR = NULL + + +# p11-kit/uri.h + +P11_KIT_URI_OK = 0 + +P11_KIT_URI_FOR_OBJECT = 2 + +p11_kit_uri_get_attributes = _libp11_kit.p11_kit_uri_get_attributes + +p11_kit_uri_any_unrecognized = _libp11_kit.p11_kit_uri_any_unrecognized + +p11_kit_uri_new = _libp11_kit.p11_kit_uri_new + +p11_kit_uri_parse = _libp11_kit.p11_kit_uri_parse + +p11_kit_uri_free = _libp11_kit.p11_kit_uri_free + + +# library.c + +def loadLibrary(module): + """Load the PKCS#11 library""" + # Load PKCS #11 library + try: + if module: + # pylint: disable=no-member + pDynLib = _ffi.dlopen(module, _ffi.RTLD_NOW | _ffi.RTLD_LOCAL) + else: + raise Exception() + + except Exception: + # Failed to load the PKCS #11 library + raise + + # Retrieve the entry point for C_GetFunctionList + pGetFunctionList = pDynLib.C_GetFunctionList + if pGetFunctionList == NULL: + raise Exception() + + # Store the handle so we can dlclose it later + + return pGetFunctionList, pDynLib + + +# p11helper.c + +# compat TODO +CKM_AES_KEY_WRAP = 0x2109 +CKM_AES_KEY_WRAP_PAD = 0x210a + +# TODO +CKA_COPYABLE = 0x0017 + +CKG_MGF1_SHA1 = 0x00000001 + +CKZ_DATA_SPECIFIED = 0x00000001 + +CK_RSA_PKCS_OAEP_PARAMS = _ffi.typeof('CK_RSA_PKCS_OAEP_PARAMS') + + +true_ptr = new_ptr(CK_BBOOL, CK_TRUE) +false_ptr = new_ptr(CK_BBOOL, CK_FALSE) + +MAX_TEMPLATE_LEN = 32 + +# +# Constants +# +CONST_RSA_PKCS_OAEP_PARAMS_ptr = new_ptr(CK_RSA_PKCS_OAEP_PARAMS, dict( + hash_alg=CKM_SHA_1, + mgf=CKG_MGF1_SHA1, + source=CKZ_DATA_SPECIFIED, + source_data=NULL, + source_data_len=0, +)) + + +# +# ipap11helper Exceptions +# +class P11HelperException(Exception): + """parent class for all exceptions""" + pass +P11HelperException.__name__ = 'Exception' + + +class Error(P11HelperException): + """general error""" + pass + + +class NotFound(P11HelperException): + """key not found""" + pass + + +class DuplicationError(P11HelperException): + """key already exists""" + pass + + +######################################################################## +# Support functions +# + +def pyobj_to_bool(pyobj): + if pyobj: + return true_ptr + return false_ptr + + +def convert_py2bool(mapping): + return tuple(pyobj_to_bool(py_obj) for py_obj in mapping) + + +def string_to_pybytes_or_none(str, len): + if str == NULL: + return None + return _ffi.buffer(str, len)[:] + + +def unicode_to_char_array(unicode): + """ + Convert a unicode string to the utf8 encoded char array + :param unicode: input python unicode object + """ + try: + utf8_str = unicode.encode('utf-8') + except Exception: + raise Error("Unable to encode UTF-8") + try: + result = new_array(unsigned_char, utf8_str) + except Exception: + raise Error("Unable to get bytes from string") + l = len(utf8_str) + return result, l + + +def char_array_to_unicode(array, l): + """ + Convert utf-8 encoded char array to unicode object + """ + return _ffi.buffer(array, l)[:].decode('utf-8') + + +def check_return_value(rv, message): + """ + Tests result value of pkc11 operations + """ + if rv != CKR_OK: + try: + errmsg = "Error at %s: 0x%x\n" % (message, rv) + except Exception: + raise Error("An error occured during error message generation. " + "Please report this problem. Developers will use " + "a crystal ball to find out the root cause.") + else: + raise Error(errmsg) + + +def _fill_template_from_parts(attr, template_len, id, id_len, label, label_len, + class_, cka_wrap, cka_unwrap): + """ + Fill template structure with pointers to attributes passed as independent + variables. + Variables with NULL values will be omitted from template. + + @warning input variables should not be modified when template is in use + """ + cnt = 0 + if label != NULL: + attr[0].type = CKA_LABEL + attr[0].pValue = label + attr[0].ulValueLen = label_len + attr += 1 + cnt += 1 + assert cnt < template_len[0] + if id != NULL: + attr[0].type = CKA_ID + attr[0].pValue = id + attr[0].ulValueLen = id_len + attr += 1 + cnt += 1 + assert cnt < template_len[0] + if cka_wrap != NULL: + attr[0].type = CKA_WRAP + attr[0].pValue = cka_wrap + attr[0].ulValueLen = sizeof(CK_BBOOL) + attr += 1 + cnt += 1 + assert cnt < template_len[0] + if cka_unwrap != NULL: + attr[0].type = CKA_UNWRAP + attr[0].pValue = cka_unwrap + attr[0].ulValueLen = sizeof(CK_BBOOL) + attr += 1 + cnt += 1 + assert cnt < template_len[0] + + if class_ != NULL: + attr[0].type = CKA_CLASS + attr[0].pValue = class_ + attr[0].ulValueLen = sizeof(CK_OBJECT_CLASS) + attr += 1 + cnt += 1 + assert cnt < template_len[0] + template_len[0] = cnt + + +def _parse_uri(uri_str): + """ + Parse string to P11-kit representation of PKCS#11 URI. + """ + uri = p11_kit_uri_new() + if not uri: + raise Error("Cannot initialize URI parser") + + try: + result = p11_kit_uri_parse(uri_str, P11_KIT_URI_FOR_OBJECT, uri) + if result != P11_KIT_URI_OK: + raise Error("Cannot parse URI") + + if p11_kit_uri_any_unrecognized(uri): + raise Error("PKCS#11 URI contains unsupported attributes") + except Error: + p11_kit_uri_free(uri) + raise + + return uri + + +def _set_wrapping_mech_parameters(mech_type, mech): + """ + Function set default param values for wrapping mechanism + :param mech_type: mechanism type + :param mech: filled structure with params based on mech type + + Warning: do not dealloc param values, it is static variables + """ + if mech_type in (CKM_RSA_PKCS, CKM_AES_KEY_WRAP, CKM_AES_KEY_WRAP_PAD): + mech.pParameter = NULL + mech.ulParameterLen = 0 + elif mech_type == CKM_RSA_PKCS_OAEP: + # Use the same configuration as openSSL + # https://www.openssl.org/docs/crypto/RSA_public_encrypt.html + mech.pParameter = CONST_RSA_PKCS_OAEP_PARAMS_ptr + mech.ulParameterLen = sizeof(CK_RSA_PKCS_OAEP_PARAMS) + else: + raise Error("Unsupported wrapping mechanism") + mech.mechanism = mech_type + + +######################################################################## +# P11_Helper object +# +class P11_Helper(object): + @property + def p11(self): + return self.p11_ptr[0] + + @property + def session(self): + return self.session_ptr[0] + + def _find_key(self, template, template_len): + """ + Find keys matching specified template. + Function returns list of key handles via objects parameter. + + :param template: PKCS#11 template for attribute matching + """ + result_objects = [] + result_object_ptr = new_ptr(CK_OBJECT_HANDLE) + objectCount_ptr = new_ptr(CK_ULONG) + + rv = self.p11.C_FindObjectsInit(self.session, template, template_len) + check_return_value(rv, "Find key init") + + rv = self.p11.C_FindObjects(self.session, result_object_ptr, 1, + objectCount_ptr) + check_return_value(rv, "Find key") + + while objectCount_ptr[0] > 0: + result_objects.append(result_object_ptr[0]) + + rv = self.p11.C_FindObjects(self.session, result_object_ptr, 1, + objectCount_ptr) + check_return_value(rv, "Check for duplicated key") + + rv = self.p11.C_FindObjectsFinal(self.session) + check_return_value(rv, "Find objects final") + + return result_objects + + def _id_exists(self, id, id_len, class_): + """ + Test if object with specified label, id and class exists + + :param id: key ID, (if value is NULL, will not be used to find key) + :param id_len: key ID length + :param class_ key: class + + :return: True if object was found, False if object doesnt exists + """ + object_count_ptr = new_ptr(CK_ULONG) + result_object_ptr = new_ptr(CK_OBJECT_HANDLE) + class_ptr = new_ptr(CK_OBJECT_CLASS, class_) + class_sec_ptr = new_ptr(CK_OBJECT_CLASS, CKO_SECRET_KEY) + + template_pub_priv = new_array(CK_ATTRIBUTE, ( + (CKA_ID, id, id_len), + (CKA_CLASS, class_ptr, sizeof(CK_OBJECT_CLASS)), + )) + + template_sec = new_array(CK_ATTRIBUTE, ( + (CKA_ID, id, id_len), + (CKA_CLASS, class_sec_ptr, sizeof(CK_OBJECT_CLASS)), + )) + + template_id = new_array(CK_ATTRIBUTE, ( + (CKA_ID, id, id_len), + )) + + # + # Only one secret key with same ID is allowed + # + if class_ == CKO_SECRET_KEY: + rv = self.p11.C_FindObjectsInit(self.session, template_id, 1) + check_return_value(rv, "id, label exists init") + + rv = self.p11.C_FindObjects(self.session, result_object_ptr, 1, + object_count_ptr) + check_return_value(rv, "id, label exists") + + rv = self.p11.C_FindObjectsFinal(self.session) + check_return_value(rv, "id, label exists final") + + if object_count_ptr[0] > 0: + return True + return False + + # + # Public and private keys can share one ID, but + # + + # test if secret key with same ID exists + rv = self.p11.C_FindObjectsInit(self.session, template_sec, 2) + check_return_value(rv, "id, label exists init") + + rv = self.p11.C_FindObjects(self.session, result_object_ptr, 1, + object_count_ptr) + check_return_value(rv, "id, label exists") + + rv = self.p11.C_FindObjectsFinal(self.session) + check_return_value(rv, "id, label exists final") + + if object_count_ptr[0] > 0: + # object found + return True + + # test if pub/private key with same id exists + object_count_ptr[0] = 0 + + rv = self.p11.C_FindObjectsInit(self.session, template_pub_priv, 2) + check_return_value(rv, "id, label exists init") + + rv = self.p11.C_FindObjects(self.session, result_object_ptr, 1, + object_count_ptr) + check_return_value(rv, "id, label exists") + + rv = self.p11.C_FindObjectsFinal(self.session) + check_return_value(rv, "id, label exists final") + + if object_count_ptr[0] > 0: + # Object found + return True + + # Object not found + return False + + def __init__(self, slot, user_pin, library_path): + self.p11_ptr = new_ptr(CK_FUNCTION_LIST_PTR) + self.session_ptr = new_ptr(CK_SESSION_HANDLE) + + self.slot = 0 + self.session_ptr[0] = 0 + self.p11_ptr[0] = NULL + self.module_handle = None + + # Parse method args + if isinstance(user_pin, unicode): + user_pin = user_pin.encode() + if isinstance(library_path, unicode): + library_path = library_path.encode() + self.slot = slot + + try: + pGetFunctionList, module_handle = loadLibrary(library_path) + except Exception: + raise Error("Could not load the library.") + + self.module_handle = module_handle + + # + # Load the function list + # + pGetFunctionList(self.p11_ptr) + + # + # Initialize + # + rv = self.p11.C_Initialize(NULL) + check_return_value(rv, "initialize") + + # + # Start session + # + rv = self.p11.C_OpenSession(self.slot, + CKF_SERIAL_SESSION | CKF_RW_SESSION, NULL, + NULL, self.session_ptr) + check_return_value(rv, "open session") + + # + # Login + # + rv = self.p11.C_Login(self.session, CKU_USER, user_pin, len(user_pin)) + check_return_value(rv, "log in") + + def finalize(self): + """ + Finalize operations with pkcs11 library + """ + if self.p11 == NULL: + return + + # + # Logout + # + rv = self.p11.C_Logout(self.session) + check_return_value(rv, "log out") + + # + # End session + # + rv = self.p11.C_CloseSession(self.session) + check_return_value(rv, "close session") + + # + # Finalize + # + self.p11.C_Finalize(NULL) + + self.p11_ptr[0] = NULL + self.session_ptr[0] = 0 + self.slot = 0 + self.module_handle = None + + ################################################################# + # Methods working with keys + # + + def generate_master_key(self, label, id, key_length=16, cka_copyable=True, + cka_decrypt=False, cka_derive=False, + cka_encrypt=False, cka_extractable=True, + cka_modifiable=True, cka_private=True, + cka_sensitive=True, cka_sign=False, + cka_unwrap=True, cka_verify=False, cka_wrap=True, + cka_wrap_with_trusted=False): + """ + Generate master key + + :return: master key handle + """ + if isinstance(id, unicode): + id = id.encode() + + attrs = ( + cka_copyable, + cka_decrypt, + cka_derive, + cka_encrypt, + cka_extractable, + cka_modifiable, + cka_private, + cka_sensitive, + cka_sign, + cka_unwrap, + cka_verify, + cka_wrap, + cka_wrap_with_trusted, + ) + + key_length_ptr = new_ptr(CK_ULONG, key_length) + master_key_ptr = new_ptr(CK_OBJECT_HANDLE) + + label_unicode = label + id_length = len(id) + id_ = new_array(CK_BYTE, id) + # TODO check long overflow + + label, label_length = unicode_to_char_array(label_unicode) + + # TODO param? + mechanism_ptr = new_ptr(CK_MECHANISM, ( + CKM_AES_KEY_GEN, NULL_PTR, 0 + )) + + if key_length not in (16, 24, 32): + raise Error("generate_master_key: key length allowed values are: " + "16, 24 and 32") + + if self._id_exists(id_, id_length, CKO_SECRET_KEY): + raise DuplicationError("Master key with same ID already exists") + + # Process keyword boolean arguments + (cka_copyable_ptr, cka_decrypt_ptr, cka_derive_ptr, cka_encrypt_ptr, + cka_extractable_ptr, cka_modifiable_ptr, cka_private_ptr, + cka_sensitive_ptr, cka_sign_ptr, cka_unwrap_ptr, cka_verify_ptr, + cka_wrap_ptr, cka_wrap_with_trusted_ptr,) = convert_py2bool(attrs) + + symKeyTemplate = new_array(CK_ATTRIBUTE, ( + (CKA_ID, id_, id_length), + (CKA_LABEL, label, label_length), + (CKA_TOKEN, true_ptr, sizeof(CK_BBOOL)), + (CKA_VALUE_LEN, key_length_ptr, sizeof(CK_ULONG)), + # TODO Softhsm doesn't support it + # (CKA_COPYABLE, cka_copyable_ptr, sizeof(CK_BBOOL)), + (CKA_DECRYPT, cka_decrypt_ptr, sizeof(CK_BBOOL)), + (CKA_DERIVE, cka_derive_ptr, sizeof(CK_BBOOL)), + (CKA_ENCRYPT, cka_encrypt_ptr, sizeof(CK_BBOOL)), + (CKA_EXTRACTABLE, cka_extractable_ptr, sizeof(CK_BBOOL)), + (CKA_MODIFIABLE, cka_modifiable_ptr, sizeof(CK_BBOOL)), + (CKA_PRIVATE, cka_private_ptr, sizeof(CK_BBOOL)), + (CKA_SENSITIVE, cka_sensitive_ptr, sizeof(CK_BBOOL)), + (CKA_SIGN, cka_sign_ptr, sizeof(CK_BBOOL)), + (CKA_UNWRAP, cka_unwrap_ptr, sizeof(CK_BBOOL)), + (CKA_VERIFY, cka_verify_ptr, sizeof(CK_BBOOL)), + (CKA_WRAP, cka_wrap_ptr, sizeof(CK_BBOOL)), + (CKA_WRAP_WITH_TRUSTED, cka_wrap_with_trusted_ptr, + sizeof(CK_BBOOL)), + )) + + rv = self.p11.C_GenerateKey(self.session, mechanism_ptr, + symKeyTemplate, + (sizeof(symKeyTemplate) // + sizeof(CK_ATTRIBUTE)), master_key_ptr) + check_return_value(rv, "generate master key") + + return master_key_ptr[0] + + def generate_replica_key_pair(self, label, id, modulus_bits=2048, + pub_cka_copyable=True, pub_cka_derive=False, + pub_cka_encrypt=False, + pub_cka_modifiable=True, + pub_cka_private=True, pub_cka_trusted=False, + pub_cka_verify=False, + pub_cka_verify_recover=False, + pub_cka_wrap=True, + priv_cka_always_authenticate=False, + priv_cka_copyable=True, + priv_cka_decrypt=False, + priv_cka_derive=False, + priv_cka_extractable=False, + priv_cka_modifiable=True, + priv_cka_private=True, + priv_cka_sensitive=True, + priv_cka_sign=False, + priv_cka_sign_recover=False, + priv_cka_unwrap=True, + priv_cka_wrap_with_trusted=False): + """ + Generate replica keys + + :returns: tuple (public_key_handle, private_key_handle) + """ + if isinstance(id, unicode): + id = id.encode() + + attrs_pub = ( + pub_cka_copyable, + pub_cka_derive, + pub_cka_encrypt, + pub_cka_modifiable, + pub_cka_private, + pub_cka_trusted, + pub_cka_verify, + pub_cka_verify_recover, + pub_cka_wrap, + ) + + attrs_priv = ( + priv_cka_always_authenticate, + priv_cka_copyable, + priv_cka_decrypt, + priv_cka_derive, + priv_cka_extractable, + priv_cka_modifiable, + priv_cka_private, + priv_cka_sensitive, + priv_cka_sign, + priv_cka_sign_recover, + priv_cka_unwrap, + priv_cka_wrap_with_trusted, + ) + + label_unicode = label + id_ = new_array(CK_BYTE, id) + id_length = len(id) + + label, label_length = unicode_to_char_array(label_unicode) + + public_key_ptr = new_ptr(CK_OBJECT_HANDLE) + private_key_ptr = new_ptr(CK_OBJECT_HANDLE) + mechanism_ptr = new_ptr(CK_MECHANISM, + (CKM_RSA_PKCS_KEY_PAIR_GEN, NULL_PTR, 0)) + + if self._id_exists(id_, id_length, CKO_PRIVATE_KEY): + raise DuplicationError("Private key with same ID already exists") + + if self._id_exists(id_, id_length, CKO_PUBLIC_KEY): + raise DuplicationError("Public key with same ID already exists") + + modulus_bits_ptr = new_ptr(CK_ULONG, modulus_bits) + + # Process keyword boolean arguments + (pub_cka_copyable_ptr, pub_cka_derive_ptr, pub_cka_encrypt_ptr, + pub_cka_modifiable_ptr, pub_cka_private_ptr, pub_cka_trusted_ptr, + pub_cka_verify_ptr, pub_cka_verify_recover_ptr, pub_cka_wrap_ptr, + ) = convert_py2bool(attrs_pub) + (priv_cka_always_authenticate_ptr, priv_cka_copyable_ptr, + priv_cka_decrypt_ptr, priv_cka_derive_ptr, priv_cka_extractable_ptr, + priv_cka_modifiable_ptr, priv_cka_private_ptr, priv_cka_sensitive_ptr, + priv_cka_sign_ptr, priv_cka_sign_recover_ptr, priv_cka_unwrap_ptr, + priv_cka_wrap_with_trusted_ptr,) = convert_py2bool(attrs_priv) + + # 65537 (RFC 6376 section 3.3.1) + public_exponent = new_array(CK_BYTE, (1, 0, 1)) + publicKeyTemplate = new_array(CK_ATTRIBUTE, ( + (CKA_ID, id_, id_length), + (CKA_LABEL, label, label_length), + (CKA_TOKEN, true_ptr, sizeof(CK_BBOOL)), + (CKA_MODULUS_BITS, modulus_bits_ptr, sizeof(CK_ULONG)), + (CKA_PUBLIC_EXPONENT, public_exponent, 3), + # TODO Softhsm doesn't support it + # (CKA_COPYABLE, pub_cka_copyable_p, sizeof(CK_BBOOL)), + (CKA_DERIVE, pub_cka_derive_ptr, sizeof(CK_BBOOL)), + (CKA_ENCRYPT, pub_cka_encrypt_ptr, sizeof(CK_BBOOL)), + (CKA_MODIFIABLE, pub_cka_modifiable_ptr, sizeof(CK_BBOOL)), + (CKA_PRIVATE, pub_cka_private_ptr, sizeof(CK_BBOOL)), + (CKA_TRUSTED, pub_cka_trusted_ptr, sizeof(CK_BBOOL)), + (CKA_VERIFY, pub_cka_verify_ptr, sizeof(CK_BBOOL)), + (CKA_VERIFY_RECOVER, pub_cka_verify_recover_ptr, sizeof(CK_BBOOL)), + (CKA_WRAP, pub_cka_wrap_ptr, sizeof(CK_BBOOL)), + )) + + privateKeyTemplate = new_array(CK_ATTRIBUTE, ( + (CKA_ID, id_, id_length), + (CKA_LABEL, label, label_length), + (CKA_TOKEN, true_ptr, sizeof(CK_BBOOL)), + (CKA_ALWAYS_AUTHENTICATE, priv_cka_always_authenticate_ptr, + sizeof(CK_BBOOL)), + # TODO Softhsm doesn't support it + # (CKA_COPYABLE, priv_cka_copyable_ptr, sizeof(CK_BBOOL)), + (CKA_DECRYPT, priv_cka_decrypt_ptr, sizeof(CK_BBOOL)), + (CKA_DERIVE, priv_cka_derive_ptr, sizeof(CK_BBOOL)), + (CKA_EXTRACTABLE, priv_cka_extractable_ptr, sizeof(CK_BBOOL)), + (CKA_MODIFIABLE, priv_cka_modifiable_ptr, sizeof(CK_BBOOL)), + (CKA_PRIVATE, priv_cka_private_ptr, sizeof(CK_BBOOL)), + (CKA_SENSITIVE, priv_cka_sensitive_ptr, sizeof(CK_BBOOL)), + (CKA_SIGN, priv_cka_sign_ptr, sizeof(CK_BBOOL)), + (CKA_SIGN_RECOVER, priv_cka_sign_ptr, sizeof(CK_BBOOL)), + (CKA_UNWRAP, priv_cka_unwrap_ptr, sizeof(CK_BBOOL)), + (CKA_WRAP_WITH_TRUSTED, priv_cka_wrap_with_trusted_ptr, + sizeof(CK_BBOOL)), + )) + + rv = self.p11.C_GenerateKeyPair(self.session, mechanism_ptr, + publicKeyTemplate, + (sizeof(publicKeyTemplate) // + sizeof(CK_ATTRIBUTE)), + privateKeyTemplate, + (sizeof(privateKeyTemplate) // + sizeof(CK_ATTRIBUTE)), + public_key_ptr, + private_key_ptr) + check_return_value(rv, "generate key pair") + + return public_key_ptr[0], private_key_ptr[0] + + def find_keys(self, objclass=CKO_VENDOR_DEFINED, label=None, id=None, + cka_wrap=None, cka_unwrap=None, uri=None): + """ + Find key + """ + if isinstance(id, unicode): + id = id.encode() + if isinstance(uri, unicode): + uri = uri.encode() + + class_ = objclass + class_ptr = new_ptr(CK_OBJECT_CLASS, class_) + ckawrap = NULL + ckaunwrap = NULL + if id is not None: + id_ = new_array(CK_BYTE, id) + id_length = len(id) + else: + id_ = NULL + id_length = 0 + label_unicode, label = label, NULL + cka_wrap_bool = cka_wrap + cka_unwrap_bool = cka_unwrap + label_length = 0 + uri_str = uri + uri = NULL + template = new_array(CK_ATTRIBUTE, MAX_TEMPLATE_LEN) + template_len_ptr = new_ptr(CK_ULONG, MAX_TEMPLATE_LEN) + + # TODO check long overflow + + if label_unicode is not None: + label, label_length = unicode_to_char_array(label_unicode) + + if cka_wrap_bool is not None: + if cka_wrap_bool: + ckawrap = true_ptr + else: + ckawrap = false_ptr + + if cka_unwrap_bool is not None: + if cka_unwrap_bool: + ckaunwrap = true_ptr + else: + ckaunwrap = false_ptr + + if class_ == CKO_VENDOR_DEFINED: + class_ptr = NULL + + try: + if uri_str is None: + _fill_template_from_parts(template, template_len_ptr, id_, + id_length, label, label_length, + class_ptr, ckawrap, ckaunwrap) + else: + uri = _parse_uri(uri_str) + template = (p11_kit_uri_get_attributes(uri, template_len_ptr)) + # Do not deallocate URI while you are using the template. + # Template contains pointers to values inside URI! + + result_list = self._find_key(template, template_len_ptr[0]) + + return result_list + finally: + if uri != NULL: + p11_kit_uri_free(uri) + + def delete_key(self, key_handle): + """ + delete key + """ + # TODO check long overflow + rv = self.p11.C_DestroyObject(self.session, key_handle) + check_return_value(rv, "object deletion") + + def _export_RSA_public_key(self, object): + """ + export RSA public key + """ + pp_ptr = new_ptr(unsigned_char_ptr, NULL) + pkey = NULL + e = NULL + n = NULL + rsa = NULL + class_ptr = new_ptr(CK_OBJECT_CLASS, CKO_PUBLIC_KEY) + key_type_ptr = new_ptr(CK_KEY_TYPE, CKK_RSA) + + obj_template = new_array(CK_ATTRIBUTE, ( + (CKA_MODULUS, NULL_PTR, 0), + (CKA_PUBLIC_EXPONENT, NULL_PTR, 0), + (CKA_CLASS, class_ptr, sizeof(CK_OBJECT_CLASS)), + (CKA_KEY_TYPE, key_type_ptr, sizeof(CK_KEY_TYPE)), + )) + + rv = self.p11.C_GetAttributeValue(self.session, object, obj_template, + (sizeof(obj_template) // + sizeof(CK_ATTRIBUTE))) + check_return_value(rv, "get RSA public key values - prepare") + + # Set proper size for attributes + modulus = new_array(CK_BYTE, + obj_template[0].ulValueLen * sizeof(CK_BYTE)) + obj_template[0].pValue = modulus + exponent = new_array(CK_BYTE, + obj_template[1].ulValueLen * sizeof(CK_BYTE)) + obj_template[1].pValue = exponent + + rv = self.p11.C_GetAttributeValue(self.session, object, obj_template, + (sizeof(obj_template) // + sizeof(CK_ATTRIBUTE))) + check_return_value(rv, "get RSA public key values") + + # Check if the key is RSA public key + if class_ptr[0] != CKO_PUBLIC_KEY: + raise Error("export_RSA_public_key: required public key class") + + if key_type_ptr[0] != CKK_RSA: + raise Error("export_RSA_public_key: required RSA key type") + + try: + rsa = RSA_new() + pkey = EVP_PKEY_new() + n = BN_bin2bn(modulus, + obj_template[0].ulValueLen * sizeof(CK_BYTE), NULL) + if n == NULL: + raise Error("export_RSA_public_key: internal error: unable to " + "convert modulus") + + e = BN_bin2bn(exponent, + obj_template[1].ulValueLen * sizeof(CK_BYTE), NULL) + if e == NULL: + raise Error("export_RSA_public_key: internal error: unable to " + "convert exponent") + + # set modulus and exponent + rsa.n = n + rsa.e = e + + if EVP_PKEY_set1_RSA(pkey, rsa) == 0: + raise Error("export_RSA_public_key: internal error: " + "EVP_PKEY_set1_RSA failed") + + pp_len = i2d_PUBKEY(pkey, pp_ptr) + ret = string_to_pybytes_or_none(pp_ptr[0], pp_len) + + return ret + finally: + if rsa != NULL: + # this frees also 'n' and 'e' + RSA_free(rsa) + else: + if n != NULL: + BN_free(n) + if e != NULL: + BN_free(e) + + if pkey != NULL: + EVP_PKEY_free(pkey) + if pp_ptr[0] != NULL: + free(pp_ptr[0]) + + def export_public_key(self, key_handle): + """ + Export public key + + Export public key in SubjectPublicKeyInfo (RFC5280) DER encoded format + """ + object = key_handle + class_ptr = new_ptr(CK_OBJECT_CLASS, CKO_PUBLIC_KEY) + key_type_ptr = new_ptr(CK_KEY_TYPE, CKK_RSA) + # TODO check long overflow + + obj_template = new_array(CK_ATTRIBUTE, ( + (CKA_CLASS, class_ptr, sizeof(CK_OBJECT_CLASS)), + (CKA_KEY_TYPE, key_type_ptr, sizeof(CK_KEY_TYPE)), + )) + + rv = self.p11.C_GetAttributeValue(self.session, object, obj_template, + (sizeof(obj_template) // + sizeof(CK_ATTRIBUTE))) + check_return_value(rv, "export_public_key: get RSA public key values") + + if class_ptr[0] != CKO_PUBLIC_KEY: + raise Error("export_public_key: required public key class") + + if key_type_ptr[0] == CKK_RSA: + return self._export_RSA_public_key(object) + else: + raise Error("export_public_key: unsupported key type") + + def _import_RSA_public_key(self, label, label_length, id, id_length, pkey, + cka_copyable, cka_derive, cka_encrypt, + cka_modifiable, cka_private, cka_trusted, + cka_verify, cka_verify_recover, cka_wrap): + """ + Import RSA public key + """ + class_ptr = new_ptr(CK_OBJECT_CLASS, CKO_PUBLIC_KEY) + keyType_ptr = new_ptr(CK_KEY_TYPE, CKK_RSA) + cka_token = true_ptr + rsa = NULL + + if pkey.type != EVP_PKEY_RSA: + raise Error("Required RSA public key") + + try: + rsa = EVP_PKEY_get1_RSA(pkey) + if rsa == NULL: + raise Error("import_RSA_public_key: EVP_PKEY_get1_RSA error") + + # convert BIGNUM to binary array + modulus = new_array(CK_BYTE, BN_num_bytes(rsa.n)) + modulus_len = BN_bn2bin(rsa.n, modulus) + if modulus_len == 0: + raise Error("import_RSA_public_key: BN_bn2bin modulus error") + + exponent = new_array(CK_BYTE, BN_num_bytes(rsa.e)) + exponent_len = BN_bn2bin(rsa.e, exponent) + if exponent_len == 0: + raise Error("import_RSA_public_key: BN_bn2bin exponent error") + + template = new_array(CK_ATTRIBUTE, ( + (CKA_ID, id, id_length), + (CKA_CLASS, class_ptr, sizeof(CK_OBJECT_CLASS)), + (CKA_KEY_TYPE, keyType_ptr, sizeof(CK_KEY_TYPE)), + (CKA_TOKEN, cka_token, sizeof(CK_BBOOL)), + (CKA_LABEL, label, label_length), + (CKA_MODULUS, modulus, modulus_len), + (CKA_PUBLIC_EXPONENT, exponent, exponent_len), + # TODO Softhsm doesn't support it + # (CKA_COPYABLE, cka_copyable, sizeof(CK_BBOOL)), + (CKA_DERIVE, cka_derive, sizeof(CK_BBOOL)), + (CKA_ENCRYPT, cka_encrypt, sizeof(CK_BBOOL)), + (CKA_MODIFIABLE, cka_modifiable, sizeof(CK_BBOOL)), + (CKA_PRIVATE, cka_private, sizeof(CK_BBOOL)), + (CKA_TRUSTED, cka_trusted, sizeof(CK_BBOOL)), + (CKA_VERIFY, cka_verify, sizeof(CK_BBOOL)), + (CKA_VERIFY_RECOVER, cka_verify_recover, sizeof(CK_BBOOL)), + (CKA_WRAP, cka_wrap, sizeof(CK_BBOOL)), + )) + object_ptr = new_ptr(CK_OBJECT_HANDLE) + + rv = self.p11.C_CreateObject(self.session, template, + (sizeof(template) // + sizeof(CK_ATTRIBUTE)), object_ptr) + check_return_value(rv, "create public key object") + + return object_ptr[0] + finally: + if rsa != NULL: + RSA_free(rsa) + + def import_public_key(self, label, id, data, cka_copyable=True, + cka_derive=False, cka_encrypt=False, + cka_modifiable=True, cka_private=True, + cka_trusted=False, cka_verify=True, + cka_verify_recover=True, cka_wrap=False): + """ + Import RSA public key + """ + if isinstance(id, unicode): + id = id.encode() + if isinstance(data, unicode): + data = data.encode() + + label_unicode = label + id_ = new_array(CK_BYTE, id) + data_ = new_array(CK_BYTE, data) + data_ptr = new_ptr(CK_BYTE_PTR, data_) + id_length = len(id) + data_length = len(data) + pkey = NULL + + attrs_pub = ( + cka_copyable, + cka_derive, + cka_encrypt, + cka_modifiable, + cka_private, + cka_trusted, + cka_verify, + cka_verify_recover, + cka_wrap, + ) + + label, label_length = unicode_to_char_array(label_unicode) + + if self._id_exists(id_, id_length, CKO_PUBLIC_KEY): + raise DuplicationError("Public key with same ID already exists") + + # Process keyword boolean arguments + (cka_copyable_ptr, cka_derive_ptr, cka_encrypt_ptr, cka_modifiable_ptr, + cka_private_ptr, cka_trusted_ptr, cka_verify_ptr, + cka_verify_recover_ptr, cka_wrap_ptr,) = convert_py2bool(attrs_pub) + + try: + # decode from ASN1 DER + pkey = d2i_PUBKEY(NULL, data_ptr, data_length) + if pkey == NULL: + raise Error("import_public_key: d2i_PUBKEY error") + if pkey.type == EVP_PKEY_RSA: + ret = self._import_RSA_public_key(label, label_length, id_, + id_length, pkey, + cka_copyable_ptr, + cka_derive_ptr, + cka_encrypt_ptr, + cka_modifiable_ptr, + cka_private_ptr, + cka_trusted_ptr, + cka_verify_ptr, + cka_verify_recover_ptr, + cka_wrap_ptr) + elif pkey.type == EVP_PKEY_DSA: + raise Error("DSA is not supported") + elif pkey.type == EVP_PKEY_EC: + raise Error("EC is not supported") + else: + raise Error("Unsupported key type") + + return ret + finally: + if pkey != NULL: + EVP_PKEY_free(pkey) + + def export_wrapped_key(self, key, wrapping_key, wrapping_mech): + """ + Export wrapped key + """ + object_key = key + object_wrapping_key = wrapping_key + wrapped_key_len_ptr = new_ptr(CK_ULONG, 0) + wrapping_mech_ptr = new_ptr(CK_MECHANISM, (wrapping_mech, NULL, 0)) + # currently we don't support parameter in mechanism + + # TODO check long overflow + # TODO export method + + # fill mech parameters + _set_wrapping_mech_parameters(wrapping_mech_ptr.mechanism, + wrapping_mech_ptr) + + rv = self.p11.C_WrapKey(self.session, wrapping_mech_ptr, + object_wrapping_key, object_key, NULL, + wrapped_key_len_ptr) + check_return_value(rv, "key wrapping: get buffer length") + + wrapped_key = new_array(CK_BYTE, wrapped_key_len_ptr[0]) + + rv = self.p11.C_WrapKey(self.session, wrapping_mech_ptr, + object_wrapping_key, object_key, wrapped_key, + wrapped_key_len_ptr) + check_return_value(rv, "key wrapping: wrapping") + + result = string_to_pybytes_or_none(wrapped_key, wrapped_key_len_ptr[0]) + + return result + + def import_wrapped_secret_key(self, label, id, data, unwrapping_key, + wrapping_mech, key_type, cka_copyable=True, + cka_decrypt=False, cka_derive=False, + cka_encrypt=False, cka_extractable=True, + cka_modifiable=True, cka_private=True, + cka_sensitive=True, cka_sign=False, + cka_unwrap=True, cka_verify=False, + cka_wrap=True, cka_wrap_with_trusted=False): + """ + Import wrapped secret key + """ + if isinstance(id, unicode): + id = id.encode() + if isinstance(data, unicode): + data = data.encode() + + wrapped_key = new_array(CK_BYTE, data) + wrapped_key_len = len(data) + unwrapping_key_object = unwrapping_key + unwrapped_key_object_ptr = new_ptr(CK_OBJECT_HANDLE, 0) + label_unicode = label + id_ = new_array(CK_BYTE, id) + id_length = len(id) + wrapping_mech_ptr = new_ptr(CK_MECHANISM, (wrapping_mech, NULL, 0)) + key_class_ptr = new_ptr(CK_OBJECT_CLASS, CKO_SECRET_KEY) + key_type_ptr = new_ptr(CK_KEY_TYPE, key_type) + + attrs = ( + cka_copyable, + cka_decrypt, + cka_derive, + cka_encrypt, + cka_extractable, + cka_modifiable, + cka_private, + cka_sensitive, + cka_sign, + cka_unwrap, + cka_verify, + cka_wrap, + cka_wrap_with_trusted, + ) + + _set_wrapping_mech_parameters(wrapping_mech_ptr.mechanism, + wrapping_mech_ptr) + + label, label_length = unicode_to_char_array(label_unicode) + + if self._id_exists(id_, id_length, key_class_ptr[0]): + raise DuplicationError("Secret key with same ID already exists") + + # Process keyword boolean arguments + (cka_copyable_ptr, cka_decrypt_ptr, cka_derive_ptr, cka_encrypt_ptr, + cka_extractable_ptr, cka_modifiable_ptr, cka_private_ptr, + cka_sensitive_ptr, cka_sign_ptr, cka_unwrap_ptr, cka_verify_ptr, + cka_wrap_ptr, cka_wrap_with_trusted_ptr,) = convert_py2bool(attrs) + + template = new_array(CK_ATTRIBUTE, ( + (CKA_CLASS, key_class_ptr, sizeof(CK_OBJECT_CLASS)), + (CKA_KEY_TYPE, key_type_ptr, sizeof(CK_KEY_TYPE)), + (CKA_ID, id_, id_length), + (CKA_LABEL, label, label_length), + (CKA_TOKEN, true_ptr, sizeof(CK_BBOOL)), + # TODO Softhsm doesn't support it + # (CKA_COPYABLE, cka_copyable_ptr, sizeof(CK_BBOOL)), + (CKA_DECRYPT, cka_decrypt_ptr, sizeof(CK_BBOOL)), + (CKA_DERIVE, cka_derive_ptr, sizeof(CK_BBOOL)), + (CKA_ENCRYPT, cka_encrypt_ptr, sizeof(CK_BBOOL)), + (CKA_EXTRACTABLE, cka_extractable_ptr, sizeof(CK_BBOOL)), + (CKA_MODIFIABLE, cka_modifiable_ptr, sizeof(CK_BBOOL)), + (CKA_PRIVATE, cka_private_ptr, sizeof(CK_BBOOL)), + (CKA_SENSITIVE, cka_sensitive_ptr, sizeof(CK_BBOOL)), + (CKA_SIGN, cka_sign_ptr, sizeof(CK_BBOOL)), + (CKA_UNWRAP, cka_unwrap_ptr, sizeof(CK_BBOOL)), + (CKA_VERIFY, cka_verify_ptr, sizeof(CK_BBOOL)), + (CKA_WRAP, cka_wrap_ptr, sizeof(CK_BBOOL)), + (CKA_WRAP_WITH_TRUSTED, cka_wrap_with_trusted_ptr, + sizeof(CK_BBOOL)), + )) + + rv = self.p11.C_UnwrapKey(self.session, wrapping_mech_ptr, + unwrapping_key_object, wrapped_key, + wrapped_key_len, template, + sizeof(template) // sizeof(CK_ATTRIBUTE), + unwrapped_key_object_ptr) + check_return_value(rv, "import_wrapped_key: key unwrapping") + + return unwrapped_key_object_ptr[0] + + def import_wrapped_private_key(self, label, id, data, unwrapping_key, + wrapping_mech, key_type, + cka_always_authenticate=False, + cka_copyable=True, cka_decrypt=False, + cka_derive=False, cka_extractable=True, + cka_modifiable=True, cka_private=True, + cka_sensitive=True, cka_sign=True, + cka_sign_recover=True, cka_unwrap=False, + cka_wrap_with_trusted=False): + """ + Import wrapped private key + """ + if isinstance(id, unicode): + id = id.encode() + if isinstance(data, unicode): + data = data.encode() + + wrapped_key = new_array(CK_BYTE, data) + wrapped_key_len = len(data) + unwrapping_key_object = unwrapping_key + unwrapped_key_object_ptr = new_ptr(CK_OBJECT_HANDLE, 0) + label_unicode = label + id_ = new_array(CK_BYTE, id) + id_length = len(id) + wrapping_mech_ptr = new_ptr(CK_MECHANISM, (wrapping_mech, NULL, 0)) + key_class_ptr = new_ptr(CK_OBJECT_CLASS, CKO_PRIVATE_KEY) + key_type_ptr = new_ptr(CK_KEY_TYPE, key_type) + + attrs_priv = ( + cka_always_authenticate, + cka_copyable, + cka_decrypt, + cka_derive, + cka_extractable, + cka_modifiable, + cka_private, + cka_sensitive, + cka_sign, + cka_sign_recover, + cka_unwrap, + cka_wrap_with_trusted, + ) + + label, label_length = unicode_to_char_array(label_unicode) + + if self._id_exists(id_, id_length, CKO_SECRET_KEY): + raise DuplicationError("Secret key with same ID already exists") + + # Process keyword boolean arguments + (cka_always_authenticate_ptr, cka_copyable_ptr, cka_decrypt_ptr, + cka_derive_ptr, cka_extractable_ptr, cka_modifiable_ptr, + cka_private_ptr, cka_sensitive_ptr, cka_sign_ptr, + cka_sign_recover_ptr, cka_unwrap_ptr, cka_wrap_with_trusted_ptr, + ) = convert_py2bool(attrs_priv) + + template = new_array(CK_ATTRIBUTE, ( + (CKA_CLASS, key_class_ptr, sizeof(CK_OBJECT_CLASS)), + (CKA_KEY_TYPE, key_type_ptr, sizeof(CK_KEY_TYPE)), + (CKA_ID, id_, id_length), + (CKA_LABEL, label, label_length), + (CKA_TOKEN, true_ptr, sizeof(CK_BBOOL)), + (CKA_ALWAYS_AUTHENTICATE, cka_always_authenticate_ptr, + sizeof(CK_BBOOL)), + # TODO Softhsm doesn't support it + # (CKA_COPYABLE, cka_copyable_ptr, sizeof(CK_BBOOL)), + (CKA_DECRYPT, cka_decrypt_ptr, sizeof(CK_BBOOL)), + (CKA_DERIVE, cka_derive_ptr, sizeof(CK_BBOOL)), + (CKA_EXTRACTABLE, cka_extractable_ptr, sizeof(CK_BBOOL)), + (CKA_MODIFIABLE, cka_modifiable_ptr, sizeof(CK_BBOOL)), + (CKA_PRIVATE, cka_private_ptr, sizeof(CK_BBOOL)), + (CKA_SENSITIVE, cka_sensitive_ptr, sizeof(CK_BBOOL)), + (CKA_SIGN, cka_sign_ptr, sizeof(CK_BBOOL)), + (CKA_SIGN_RECOVER, cka_sign_ptr, sizeof(CK_BBOOL)), + (CKA_UNWRAP, cka_unwrap_ptr, sizeof(CK_BBOOL)), + (CKA_WRAP_WITH_TRUSTED, cka_wrap_with_trusted_ptr, + sizeof(CK_BBOOL)), + )) + + rv = self.p11.C_UnwrapKey(self.session, wrapping_mech_ptr, + unwrapping_key_object, wrapped_key, + wrapped_key_len, template, + sizeof(template) // sizeof(CK_ATTRIBUTE), + unwrapped_key_object_ptr) + check_return_value(rv, "import_wrapped_key: key unwrapping") + + return unwrapped_key_object_ptr[0] + + def set_attribute(self, key_object, attr, value): + """ + Set object attributes + """ + object = key_object + attribute_ptr = new_ptr(CK_ATTRIBUTE) + + attribute_ptr.type = attr + if attr in (CKA_ALWAYS_AUTHENTICATE, + CKA_ALWAYS_SENSITIVE, + CKA_COPYABLE, + CKA_ENCRYPT, + CKA_EXTRACTABLE, + CKA_DECRYPT, + CKA_DERIVE, + CKA_LOCAL, + CKA_MODIFIABLE, + CKA_NEVER_EXTRACTABLE, + CKA_PRIVATE, + CKA_SENSITIVE, + CKA_SIGN, + CKA_SIGN_RECOVER, + CKA_TOKEN, + CKA_TRUSTED, + CKA_UNWRAP, + CKA_VERIFY, + CKA_VERIFY_RECOVER, + CKA_WRAP, + CKA_WRAP_WITH_TRUSTED): + attribute_ptr.pValue = true_ptr if value else false_ptr + attribute_ptr.ulValueLen = sizeof(CK_BBOOL) + elif attr == CKA_ID: + if not isinstance(value, bytes): + raise Error("Bytestring value expected") + attribute_ptr.pValue = new_array(CK_BYTE, value) + attribute_ptr.ulValueLen = len(value) + elif attr == CKA_LABEL: + if not isinstance(value, unicode): + raise Error("Unicode value expected") + label, label_length = unicode_to_char_array(value) + attribute_ptr.pValue = label + attribute_ptr.ulValueLen = label_length + elif attr == CKA_KEY_TYPE: + if not isinstance(value, int): + raise Error("Integer value expected") + attribute_ptr.pValue = new_ptr(unsigned_long, value) + attribute_ptr.ulValueLen = sizeof(unsigned_long) + else: + raise Error("Unknown attribute") + + template = new_array(CK_ATTRIBUTE, (attribute_ptr[0],)) + + rv = self.p11.C_SetAttributeValue(self.session, object, template, + (sizeof(template) // + sizeof(CK_ATTRIBUTE))) + check_return_value(rv, "set_attribute") + + def get_attribute(self, key_object, attr): + object = key_object + attribute_ptr = new_ptr(CK_ATTRIBUTE) + + attribute_ptr.type = attr + attribute_ptr.pValue = NULL_PTR + attribute_ptr.ulValueLen = 0 + template = new_array(CK_ATTRIBUTE, (attribute_ptr[0],)) + + rv = self.p11.C_GetAttributeValue(self.session, object, template, + (sizeof(template) // + sizeof(CK_ATTRIBUTE))) + if rv == CKR_ATTRIBUTE_TYPE_INVALID or template[0].ulValueLen == -1: + raise NotFound("attribute does not exist") + check_return_value(rv, "get_attribute init") + value = new_array(unsigned_char, template[0].ulValueLen) + template[0].pValue = value + + rv = self.p11.C_GetAttributeValue(self.session, object, template, + (sizeof(template) // + sizeof(CK_ATTRIBUTE))) + check_return_value(rv, "get_attribute") + + if attr in (CKA_ALWAYS_AUTHENTICATE, + CKA_ALWAYS_SENSITIVE, + CKA_COPYABLE, + CKA_ENCRYPT, + CKA_EXTRACTABLE, + CKA_DECRYPT, + CKA_DERIVE, + CKA_LOCAL, + CKA_MODIFIABLE, + CKA_NEVER_EXTRACTABLE, + CKA_PRIVATE, + CKA_SENSITIVE, + CKA_SIGN, + CKA_SIGN_RECOVER, + CKA_TOKEN, + CKA_TRUSTED, + CKA_UNWRAP, + CKA_VERIFY, + CKA_VERIFY_RECOVER, + CKA_WRAP, + CKA_WRAP_WITH_TRUSTED): + ret = bool(_ffi.cast(_ffi.getctype(CK_BBOOL, '*'), value)[0]) + elif attr == CKA_LABEL: + ret = char_array_to_unicode(value, template[0].ulValueLen) + elif attr in (CKA_MODULUS, CKA_PUBLIC_EXPONENT, CKA_ID): + ret = string_to_pybytes_or_none(value, template[0].ulValueLen) + elif attr == CKA_KEY_TYPE: + ret = _ffi.cast(_ffi.getctype(unsigned_long, '*'), value)[0] + else: + raise Error("Unknown attribute") + + return ret + + +# Key Classes +KEY_CLASS_PUBLIC_KEY = CKO_PUBLIC_KEY +KEY_CLASS_PRIVATE_KEY = CKO_PRIVATE_KEY +KEY_CLASS_SECRET_KEY = CKO_SECRET_KEY + +# Key types +KEY_TYPE_RSA = CKK_RSA +KEY_TYPE_AES = CKK_AES + +# Wrapping mech type +MECH_RSA_PKCS = CKM_RSA_PKCS +MECH_RSA_PKCS_OAEP = CKM_RSA_PKCS_OAEP +MECH_AES_KEY_WRAP = CKM_AES_KEY_WRAP +MECH_AES_KEY_WRAP_PAD = CKM_AES_KEY_WRAP_PAD + def generate_master_key(p11, keylabel=u"dnssec-master", key_length=16, disable_old_keys=True): - assert isinstance(p11, _ipap11helper.P11_Helper) + assert isinstance(p11, P11_Helper) key_id = None while True: # check if key with this ID exist in LDAP or softHSM # id is 16 Bytes long key_id = "".join(chr(random.randint(0, 255)) for _ in range(0, 16)) - keys = p11.find_keys(_ipap11helper.KEY_CLASS_SECRET_KEY, + keys = p11.find_keys(KEY_CLASS_SECRET_KEY, label=keylabel, id=key_id) if not keys: @@ -28,12 +1886,12 @@ def generate_master_key(p11, keylabel=u"dnssec-master", key_length=16, if disable_old_keys: # set CKA_WRAP=False for old master keys - master_keys = p11.find_keys(_ipap11helper.KEY_CLASS_SECRET_KEY, + master_keys = p11.find_keys(KEY_CLASS_SECRET_KEY, label=keylabel, cka_wrap=True) for handle in master_keys: # don't disable wrapping for new key # compare IDs not handle - if key_id != p11.get_attribute(handle, _ipap11helper.CKA_ID): - p11.set_attribute(handle, _ipap11helper.CKA_WRAP, False) + if key_id != p11.get_attribute(handle, CKA_ID): + p11.set_attribute(handle, CKA_WRAP, False) diff --git a/ipaserver/install/dnskeysyncinstance.py b/ipaserver/install/dnskeysyncinstance.py index 4ae2d2895..a5871bad7 100644 --- a/ipaserver/install/dnskeysyncinstance.py +++ b/ipaserver/install/dnskeysyncinstance.py @@ -13,7 +13,7 @@ import stat import ldap -import _ipap11helper +from ipapython import p11helper as _ipap11helper from ipapython.dnsutil import DNSName from ipaserver.install import service from ipaserver.install import installutils diff --git a/ipaserver/install/opendnssecinstance.py b/ipaserver/install/opendnssecinstance.py index 4d3b65ff9..c7a796e8e 100644 --- a/ipaserver/install/opendnssecinstance.py +++ b/ipaserver/install/opendnssecinstance.py @@ -9,7 +9,7 @@ import stat import shutil from subprocess import CalledProcessError -import _ipap11helper +from ipapython import p11helper as _ipap11helper from ipaserver.install import service from ipaserver.install import installutils from ipapython.ipa_log_manager import root_logger diff --git a/ipatests/pytest.ini b/ipatests/pytest.ini index e38858bc7..531eb847e 100644 --- a/ipatests/pytest.ini +++ b/ipatests/pytest.ini @@ -21,7 +21,6 @@ addopts = --doctest-modules --ignore=doc/examples/python-api.py --ignore=install/share/copy-schema-to-ca.py --ignore=install/share/wsgi.py - --ignore=ipapython/ipap11helper/setup.py markers = tier0: basic unit tests and critical functionality tier1: functional API tests diff --git a/ipatests/test_ipapython/test_ipap11helper.py b/ipatests/test_ipapython/test_ipap11helper.py index 0b442b553..2c8fd2892 100644 --- a/ipatests/test_ipapython/test_ipap11helper.py +++ b/ipatests/test_ipapython/test_ipap11helper.py @@ -17,7 +17,7 @@ import tempfile import pytest from ipaplatform.paths import paths -import _ipap11helper +from ipapython import p11helper as _ipap11helper pytestmark = pytest.mark.tier0 |