diff --git a/PyKCS11/__init__.py b/PyKCS11/__init__.py index a7b8084..9aaed3f 100644 --- a/PyKCS11/__init__.py +++ b/PyKCS11/__init__.py @@ -31,6 +31,7 @@ CKA = {} CKC = {} +CKD = {} CKF = {} CKG = {} CKH = {} @@ -46,6 +47,7 @@ for x in PyKCS11.LowLevel.__dict__.keys(): if x[:4] == 'CKA_' \ or x[:4] == 'CKC_' \ + or x[:4] == 'CKD_' \ or x[:4] == 'CKF_' \ or x[:4] == 'CKG_' \ or x[:4] == 'CKH_' \ @@ -827,6 +829,39 @@ def __init__(self, mecha, hashAlg, mgf, sLen): def to_native(self): return self._mech +class ECDH1_DERIVE_Mechanism(object): + """CKM_ECDH1_DERIVE key derivation mechanism""" + + def __init__(self, publicData, kdf = CKD_NULL, sharedData = None): + """ + :param publicData: Other party public key which is EC Point [PC || coord-x || coord-y]. + :param kdf: Key derivation function. OPTIONAL. Defaults to CKD_NULL + :param sharedData: additional shared data. OPTIONAL + """ + self._param = PyKCS11.LowLevel.CK_ECDH1_DERIVE_PARAMS() + + self._param.kdf = kdf + + if sharedData: + self._shared_data = ckbytelist(sharedData) + self._param.pSharedData = self._shared_data + self._param.ulSharedDataLen = len(self._shared_data) + else: + self._source_shared_data = None + self._param.ulSharedDataLen = 0 + + self._public_data = ckbytelist(publicData) + self._param.pPublicData = self._public_data + self._param.ulPublicDataLen = len(self._public_data) + + self._mech = PyKCS11.LowLevel.CK_MECHANISM() + self._mech.mechanism = CKM_ECDH1_DERIVE + self._mech.pParameter = self._param + self._mech.ulParameterLen = PyKCS11.LowLevel.CK_ECDH1_DERIVE_PARAMS_LENGTH + + def to_native(self): + return self._mech + class DigestSession(object): def __init__(self, lib, session, mecha): @@ -1255,6 +1290,26 @@ def unwrapKey(self, unwrappingKey, wrappedKey, template, raise PyKCS11Error(rv) return handle + def deriveKey(self, baseKey, template, mecha): + """ + C_DeriveKey + :param baseKey: the base key handle + :type baseKey: integer + :param template: template for the unwrapped key + :param mecha: the decrypt mechanism to be used (use + `ECDH1_DERIVE_Mechanism(...)` for `CKM_ECDH1_DERIVE`) + :type mecha: :class:`Mechanism` + :return: the unwrapped key object + :rtype: integer + """ + m = mecha.to_native() + handle = PyKCS11.LowLevel.CK_OBJECT_HANDLE() + attrs = self._template2ckattrlist(template) + rv = self.lib.C_DeriveKey(self.session, m, baseKey, attrs, handle) + if rv != CKR_OK: + raise PyKCS11Error(rv) + return handle + def isNum(self, type): """ is the type a numerical value? diff --git a/src/opensc/pkcs11.h b/src/opensc/pkcs11.h index a60d885..4678ed5 100644 --- a/src/opensc/pkcs11.h +++ b/src/opensc/pkcs11.h @@ -682,6 +682,34 @@ typedef unsigned long ck_mechanism_type_t; #define CKG_MGF1_SHA384 (0x00000003) #define CKG_MGF1_SHA512 (0x00000004) +#define CKD_NULL (0x00000001) +#define CKD_SHA1_KDF (0x00000002) + +/* The following X9.42 DH key derivation functions are defined */ +#define CKD_SHA1_KDF_ASN1 (0x00000003UL) +#define CKD_SHA1_KDF_CONCATENATE (0x00000004UL) +#define CKD_SHA224_KDF (0x00000005UL) +#define CKD_SHA256_KDF (0x00000006UL) +#define CKD_SHA384_KDF (0x00000007UL) +#define CKD_SHA512_KDF (0x00000008UL) +#define CKD_CPDIVERSIFY_KDF (0x00000009UL) +#define CKD_SHA3_224_KDF (0x0000000AUL) +#define CKD_SHA3_256_KDF (0x0000000BUL) +#define CKD_SHA3_384_KDF (0x0000000CUL) +#define CKD_SHA3_512_KDF (0x0000000DUL) +#define CKD_SHA1_KDF_SP800 (0x0000000EUL) +#define CKD_SHA224_KDF_SP800 (0x0000000FUL) +#define CKD_SHA256_KDF_SP800 (0x00000010UL) +#define CKD_SHA384_KDF_SP800 (0x00000011UL) +#define CKD_SHA512_KDF_SP800 (0x00000012UL) +#define CKD_SHA3_224_KDF_SP800 (0x00000013UL) +#define CKD_SHA3_256_KDF_SP800 (0x00000014UL) +#define CKD_SHA3_384_KDF_SP800 (0x00000015UL) +#define CKD_SHA3_512_KDF_SP800 (0x00000016UL) +#define CKD_BLAKE2B_160_KDF (0x00000017UL) +#define CKD_BLAKE2B_256_KDF (0x00000018UL) +#define CKD_BLAKE2B_384_KDF (0x00000019UL) +#define CKD_BLAKE2B_512_KDF (0x0000001aUL) struct ck_mechanism { @@ -722,6 +750,14 @@ struct ck_gcm_params { unsigned long ulTagBits; } ; +struct ck_ecdh1_derive_params { + unsigned long kdf; + unsigned long ulSharedDataLen; + void * pSharedData; + unsigned long ulPublicDataLen; + void * pPublicData; +} ; + #define CKF_HW (1 << 0) #define CKF_ENCRYPT (1 << 8) #define CKF_DECRYPT (1 << 9) @@ -1293,6 +1329,9 @@ typedef struct ck_rsa_pkcs_pss_params *CK_RSA_PKCS_PSS_PARAMS_PTR; typedef struct ck_gcm_params CK_GCM_PARAMS; +typedef struct ck_ecdh1_derive_params CK_ECDH1_DERIVE_PARAMS; +typedef struct ck_ecdh1_derive_params *CK_ECDH1_DERIVE_PARAMS_PTR; + typedef struct ck_function_list CK_FUNCTION_LIST; typedef struct ck_function_list *CK_FUNCTION_LIST_PTR; typedef struct ck_function_list **CK_FUNCTION_LIST_PTR_PTR; diff --git a/src/pkcs11lib.cpp b/src/pkcs11lib.cpp index 86dd8ee..c5f6cb7 100644 --- a/src/pkcs11lib.cpp +++ b/src/pkcs11lib.cpp @@ -920,6 +920,33 @@ CK_RV CPKCS11Lib::C_UnwrapKey( return rv; } +CK_RV CPKCS11Lib::C_DeriveKey( + CK_SESSION_HANDLE hSession, + CK_MECHANISM *pMechanism, + CK_OBJECT_HANDLE hBaseKey, + vector Template, + CK_OBJECT_HANDLE & outKey) +{ + CPKCS11LIB_PROLOGUE(C_DeriveKey); + CK_OBJECT_HANDLE hKey = static_cast(outKey); + + CK_ULONG ulAttributeCount = 0; + CK_ATTRIBUTE* pTemplate = AttrVector2Template(Template, ulAttributeCount); + + rv = m_pFunc->C_DeriveKey(hSession, + pMechanism, + hBaseKey, + pTemplate, + ulAttributeCount, + &hKey); + + if (pTemplate) + DestroyTemplate(pTemplate, ulAttributeCount); + outKey = static_cast(hKey); + CPKCS11LIB_EPILOGUE; + return rv; +} + CK_RV CPKCS11Lib::C_SeedRandom( CK_SESSION_HANDLE hSession, vector Seed) diff --git a/src/pkcs11lib.h b/src/pkcs11lib.h index 2c53679..c0141b1 100644 --- a/src/pkcs11lib.h +++ b/src/pkcs11lib.h @@ -266,6 +266,13 @@ class CPKCS11Lib vector Template, CK_OBJECT_HANDLE & outhKey); + CK_RV C_DeriveKey( + CK_SESSION_HANDLE hSession, + CK_MECHANISM *pMechanism, + CK_OBJECT_HANDLE hBaseKey, + vector Template, + CK_OBJECT_HANDLE & outkey); + CK_RV C_SeedRandom( CK_SESSION_HANDLE hSession, vector Seed); diff --git a/src/pykcs11.i b/src/pykcs11.i index b5ee593..b053b36 100644 --- a/src/pykcs11.i +++ b/src/pykcs11.i @@ -252,7 +252,10 @@ typedef struct CK_DATE{ if (!SWIG_IsOK(res2)) { res2 = SWIG_ConvertPtr($input, &arg2, $descriptor(CK_GCM_PARAMS*), 0); if (!SWIG_IsOK(res2)) { - SWIG_exception_fail(SWIG_ArgError(res2), "unsupported CK_MECHANISM Parameter type."); + res2 = SWIG_ConvertPtr($input, &arg2, $descriptor(CK_ECDH1_DERIVE_PARAMS*), 0); + if (!SWIG_IsOK(res2)) { + SWIG_exception_fail(SWIG_ArgError(res2), "unsupported CK_MECHANISM Parameter type."); + } } } } @@ -359,6 +362,31 @@ typedef struct CK_RSA_PKCS_PSS_PARAMS { %constant int CK_RSA_PKCS_PSS_PARAMS_LENGTH = sizeof(CK_RSA_PKCS_PSS_PARAMS); +typedef struct CK_ECDH1_DERIVE_PARAMS { + unsigned long kdf; + unsigned long ulSharedDataLen; + void* pSharedData; + unsigned long ulPublicDataLen; + void* pPublicData; +} CK_ECDH1_DERIVE_PARAMS; + +%extend CK_ECDH1_DERIVE_PARAMS +{ + CK_ECDH1_DERIVE_PARAMS() + { + CK_ECDH1_DERIVE_PARAMS *p = new CK_ECDH1_DERIVE_PARAMS(); + p->kdf = CKD_NULL; + p->pSharedData = NULL; + p->ulSharedDataLen = 0; + p->pPublicData = NULL; + p->ulPublicDataLen = 0; + + return p; + } +}; + +%constant int CK_ECDH1_DERIVE_PARAMS_LENGTH = sizeof(CK_ECDH1_DERIVE_PARAMS); + typedef struct CK_MECHANISM_INFO { %immutable; unsigned long ulMinKeySize; diff --git a/test/test_derive.py b/test/test_derive.py new file mode 100644 index 0000000..71929a4 --- /dev/null +++ b/test/test_derive.py @@ -0,0 +1,144 @@ +import unittest +from asn1crypto.keys import ECDomainParameters, NamedCurve +from PyKCS11 import PyKCS11 + +class TestUtil(unittest.TestCase): + def setUp(self): + self.pkcs11 = PyKCS11.PyKCS11Lib() + self.pkcs11.load() + + # get SoftHSM major version + info = self.pkcs11.getInfo() + self.SoftHSMversion = info.libraryVersion[0] + self.manufacturer = info.manufacturerID + + self.slot = self.pkcs11.getSlotList(tokenPresent=True)[0] + self.session = self.pkcs11.openSession( + self.slot, PyKCS11.CKF_SERIAL_SESSION | PyKCS11.CKF_RW_SESSION + ) + self.session.login("1234") + + # Select the curve to be used for the keys + curve = u"secp256r1" + + # Setup the domain parameters, unicode conversion needed + # for the curve string + domain_params = ECDomainParameters(name="named", value=NamedCurve(curve)) + self.ecParams = domain_params.dump() + + keyID = (0x01,) + baseKeyPubTemplate = [ + (PyKCS11.CKA_CLASS, PyKCS11.CKO_PUBLIC_KEY), + (PyKCS11.CKA_PRIVATE, PyKCS11.CK_FALSE), + (PyKCS11.CKA_TOKEN, PyKCS11.CK_TRUE), + (PyKCS11.CKA_ENCRYPT, PyKCS11.CK_TRUE), + (PyKCS11.CKA_VERIFY, PyKCS11.CK_TRUE), + (PyKCS11.CKA_WRAP, PyKCS11.CK_TRUE), + (PyKCS11.CKA_KEY_TYPE, PyKCS11.CKK_ECDSA), + (PyKCS11.CKA_EC_PARAMS, self.ecParams), + (PyKCS11.CKA_LABEL, "TestBaseKeyP256"), + (PyKCS11.CKA_ID, keyID), + ] + baseKeyPvtTemplate = [ + (PyKCS11.CKA_CLASS, PyKCS11.CKO_PRIVATE_KEY), + (PyKCS11.CKA_KEY_TYPE, PyKCS11.CKK_ECDSA), + (PyKCS11.CKA_TOKEN, PyKCS11.CK_TRUE), + (PyKCS11.CKA_SENSITIVE, PyKCS11.CK_TRUE), + (PyKCS11.CKA_PRIVATE, PyKCS11.CK_FALSE), + (PyKCS11.CKA_DECRYPT, PyKCS11.CK_TRUE), + (PyKCS11.CKA_SIGN, PyKCS11.CK_TRUE), + (PyKCS11.CKA_UNWRAP, PyKCS11.CK_TRUE), + (PyKCS11.CKA_LABEL, "TestBaseKeyP256"), + (PyKCS11.CKA_ID, keyID), + (PyKCS11.CKA_DERIVE, PyKCS11.CK_TRUE), + ] + mechanism = PyKCS11.Mechanism(PyKCS11.CKM_EC_KEY_PAIR_GEN, None) + self.baseEcPubKey, self.baseEcPvtKey = self.session.generateKeyPair(baseKeyPubTemplate, baseKeyPvtTemplate, mechanism) + self.assertIsNotNone(self.baseEcPubKey) + self.assertIsNotNone(self.baseEcPvtKey) + + def tearDown(self): + self.session.destroyObject(self.baseEcPubKey) + self.session.destroyObject(self.baseEcPvtKey) + + self.session.logout() + self.pkcs11.closeAllSessions(self.slot) + del self.pkcs11 + + def test_deriveKey_ECDH1_DERIVE(self): + if self.SoftHSMversion < 2: + self.skipTest("generateKeyPair() only supported by SoftHSM >= 2") + + keyID = (0x11,) + pubTemplate = [ + (PyKCS11.CKA_CLASS, PyKCS11.CKO_PUBLIC_KEY), + (PyKCS11.CKA_PRIVATE, PyKCS11.CK_FALSE), + (PyKCS11.CKA_TOKEN, PyKCS11.CK_FALSE), + (PyKCS11.CKA_ENCRYPT, PyKCS11.CK_TRUE), + (PyKCS11.CKA_VERIFY, PyKCS11.CK_TRUE), + (PyKCS11.CKA_WRAP, PyKCS11.CK_TRUE), + (PyKCS11.CKA_KEY_TYPE, PyKCS11.CKK_ECDSA), + (PyKCS11.CKA_EC_PARAMS, self.ecParams), + (PyKCS11.CKA_LABEL, "testKeyP256"), + (PyKCS11.CKA_ID, keyID), + ] + pvtTemplate = [ + (PyKCS11.CKA_CLASS, PyKCS11.CKO_PRIVATE_KEY), + (PyKCS11.CKA_KEY_TYPE, PyKCS11.CKK_ECDSA), + (PyKCS11.CKA_TOKEN, PyKCS11.CK_FALSE), + (PyKCS11.CKA_SENSITIVE, PyKCS11.CK_TRUE), + (PyKCS11.CKA_PRIVATE, PyKCS11.CK_TRUE), + (PyKCS11.CKA_DECRYPT, PyKCS11.CK_TRUE), + (PyKCS11.CKA_SIGN, PyKCS11.CK_TRUE), + (PyKCS11.CKA_UNWRAP, PyKCS11.CK_TRUE), + (PyKCS11.CKA_LABEL, "testKeyP256"), + (PyKCS11.CKA_ID, keyID), + (PyKCS11.CKA_DERIVE, PyKCS11.CK_TRUE), + ] + mechanism = PyKCS11.Mechanism(PyKCS11.CKM_EC_KEY_PAIR_GEN, None) + pubKey, pvtKey = self.session.generateKeyPair(pubTemplate, pvtTemplate, mechanism) + self.assertIsNotNone(pubKey) + self.assertIsNotNone(pvtKey) + + keyID = (0x22,) + derivedAESKeyTemplate = [ + (PyKCS11.CKA_CLASS, PyKCS11.CKO_SECRET_KEY), + (PyKCS11.CKA_KEY_TYPE, PyKCS11.CKK_AES), + (PyKCS11.CKA_TOKEN, PyKCS11.CK_FALSE), + (PyKCS11.CKA_SENSITIVE, PyKCS11.CK_TRUE), + (PyKCS11.CKA_PRIVATE, PyKCS11.CK_TRUE), + (PyKCS11.CKA_ENCRYPT, PyKCS11.CK_TRUE), + (PyKCS11.CKA_DECRYPT, PyKCS11.CK_TRUE), + (PyKCS11.CKA_SIGN, PyKCS11.CK_FALSE), + (PyKCS11.CKA_EXTRACTABLE, PyKCS11.CK_TRUE), + (PyKCS11.CKA_VERIFY, PyKCS11.CK_FALSE), + (PyKCS11.CKA_VALUE_LEN, 24), + (PyKCS11.CKA_LABEL, "derivedAESKey"), + (PyKCS11.CKA_ID, keyID), + ] + + # derive key 1 : self.basePvtKey + pubKey + attrs = self.session.getAttributeValue(pubKey, [PyKCS11.CKA_EC_POINT], True) + mechanism = PyKCS11.ECDH1_DERIVE_Mechanism(bytes(attrs[0])) + derivedKey = self.session.deriveKey(self.baseEcPvtKey, derivedAESKeyTemplate, mechanism) + self.assertIsNotNone(derivedKey) + + # derive key 2 : pvtKey + self.basePubKey + attrs = self.session.getAttributeValue(self.baseEcPubKey, [PyKCS11.CKA_EC_POINT], True) + mechanism = PyKCS11.ECDH1_DERIVE_Mechanism(bytes(attrs[0])) + derivedKey2 = self.session.deriveKey(pvtKey, derivedAESKeyTemplate, mechanism) + self.assertIsNotNone(derivedKey2) + + DataIn = "Sample data to test ecdh1 derive".encode("utf-8") + mechanism = PyKCS11.Mechanism(PyKCS11.CKM_AES_CBC, "1234567812345678") + DataOut = self.session.encrypt(derivedKey, DataIn, mechanism) + DataCheck = self.session.decrypt(derivedKey2, DataOut, mechanism) + + # match check values + self.assertSequenceEqual(DataIn, DataCheck) + + # cleanup + self.session.destroyObject(derivedKey) + self.session.destroyObject(derivedKey2) + self.session.destroyObject(pubKey) + self.session.destroyObject(pvtKey)