Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Adds C_DeriveKey() support with CKM_ECDH1_DERIVE #95

Merged
merged 2 commits into from
Aug 6, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
55 changes: 55 additions & 0 deletions PyKCS11/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@

CKA = {}
CKC = {}
CKD = {}
CKF = {}
CKG = {}
CKH = {}
Expand All @@ -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_' \
Expand Down Expand Up @@ -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):
Expand Down Expand Up @@ -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?
Expand Down
39 changes: 39 additions & 0 deletions src/opensc/pkcs11.h
Original file line number Diff line number Diff line change
Expand Up @@ -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
{
Expand Down Expand Up @@ -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)
Expand Down Expand Up @@ -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;
Expand Down
27 changes: 27 additions & 0 deletions src/pkcs11lib.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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<CK_ATTRIBUTE_SMART> Template,
CK_OBJECT_HANDLE & outKey)
{
CPKCS11LIB_PROLOGUE(C_DeriveKey);
CK_OBJECT_HANDLE hKey = static_cast<CK_OBJECT_HANDLE>(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<CK_OBJECT_HANDLE>(hKey);
CPKCS11LIB_EPILOGUE;
return rv;
}

CK_RV CPKCS11Lib::C_SeedRandom(
CK_SESSION_HANDLE hSession,
vector<unsigned char> Seed)
Expand Down
7 changes: 7 additions & 0 deletions src/pkcs11lib.h
Original file line number Diff line number Diff line change
Expand Up @@ -266,6 +266,13 @@ class CPKCS11Lib
vector<CK_ATTRIBUTE_SMART> Template,
CK_OBJECT_HANDLE & outhKey);

CK_RV C_DeriveKey(
CK_SESSION_HANDLE hSession,
CK_MECHANISM *pMechanism,
CK_OBJECT_HANDLE hBaseKey,
vector<CK_ATTRIBUTE_SMART> Template,
CK_OBJECT_HANDLE & outkey);

CK_RV C_SeedRandom(
CK_SESSION_HANDLE hSession,
vector<unsigned char> Seed);
Expand Down
30 changes: 29 additions & 1 deletion src/pykcs11.i
Original file line number Diff line number Diff line change
Expand Up @@ -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.");
}
}
}
}
Expand Down Expand Up @@ -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;
Expand Down
144 changes: 144 additions & 0 deletions test/test_derive.py
Original file line number Diff line number Diff line change
@@ -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)