• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

LudovicRousseau / PyKCS11 / 16367380199

18 Jul 2025 09:31AM UTC coverage: 93.474% (+5.9%) from 87.592%
16367380199

push

github

LudovicRousseau
IsNum(): CKA_HW_FEATURE_TYPE is also a numeric value

Thanks to Ivan Wallis for the bug report
"Venafi PKCS#11 Library fails with Unkown PKCS#11 type () during findObjects #140"
Closes: https://github.com/LudovicRousseau/PyKCS11/issues/140

5529 of 5915 relevant lines covered (93.47%)

0.93 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

56.25
/test/test_derive.py
1
# pylint: disable=missing-module-docstring
2
# pylint: disable=missing-class-docstring
3
# pylint: disable=missing-function-docstring
4

5
import unittest
1✔
6

7
from asn1crypto.keys import ECDomainParameters, NamedCurve
1✔
8

9
from PyKCS11 import PyKCS11
1✔
10

11

12
class TestUtil(unittest.TestCase):
1✔
13
    def setUp(self):
1✔
14
        self.pkcs11 = PyKCS11.PyKCS11Lib()
1✔
15
        self.pkcs11.load()
1✔
16

17
        # get SoftHSM major version
18
        info = self.pkcs11.getInfo()
1✔
19
        self.SoftHSMversion = info.libraryVersion
1✔
20
        self.manufacturer = info.manufacturerID
1✔
21

22
        self.slot = self.pkcs11.getSlotList(tokenPresent=True)[0]
1✔
23
        self.session = self.pkcs11.openSession(
1✔
24
            self.slot, PyKCS11.CKF_SERIAL_SESSION | PyKCS11.CKF_RW_SESSION
25
        )
26
        self.session.login("1234")
1✔
27

28
        # common templates used in derive test cases
29
        self.aesKeyTemplate = [
1✔
30
            (PyKCS11.CKA_CLASS, PyKCS11.CKO_SECRET_KEY),
31
            (PyKCS11.CKA_KEY_TYPE, PyKCS11.CKK_AES),
32
            (PyKCS11.CKA_VALUE_LEN, 32),
33
            (PyKCS11.CKA_TOKEN, PyKCS11.CK_TRUE),
34
            (PyKCS11.CKA_SENSITIVE, PyKCS11.CK_FALSE),
35
            (PyKCS11.CKA_EXTRACTABLE, PyKCS11.CK_TRUE),
36
            (PyKCS11.CKA_PRIVATE, PyKCS11.CK_TRUE),
37
            (PyKCS11.CKA_LABEL, "DeriveTestBaseAes256Key"),
38
            (PyKCS11.CKA_DERIVE, PyKCS11.CK_TRUE),
39
        ]
40

41
        self.genericKeyTemplate = [
1✔
42
            (PyKCS11.CKA_CLASS, PyKCS11.CKO_SECRET_KEY),
43
            (PyKCS11.CKA_KEY_TYPE, PyKCS11.CKK_GENERIC_SECRET),
44
            (PyKCS11.CKA_TOKEN, PyKCS11.CK_FALSE),
45
            (PyKCS11.CKA_SENSITIVE, PyKCS11.CK_FALSE),
46
            (PyKCS11.CKA_EXTRACTABLE, PyKCS11.CK_TRUE),
47
            (PyKCS11.CKA_PRIVATE, PyKCS11.CK_TRUE),
48
            (PyKCS11.CKA_LABEL, "DeriveTestGenericKey"),
49
        ]
50

51
        # generate a common symmetric base key for tests
52
        keyID = (0x01,)
1✔
53
        baseKeyTemplate = self.aesKeyTemplate + [(PyKCS11.CKA_ID, keyID)]
1✔
54
        mechanism = PyKCS11.Mechanism(PyKCS11.CKM_AES_KEY_GEN, None)
1✔
55
        self.baseKey = self.session.generateKey(baseKeyTemplate, mechanism)
1✔
56
        self.assertIsNotNone(self.baseKey)
1✔
57

58
        # Select the curve to be used for the keys
59
        curve = "secp256r1"
1✔
60

61
        # Setup the domain parameters, unicode conversion needed
62
        # for the curve string
63
        domain_params = ECDomainParameters(name="named", value=NamedCurve(curve))
1✔
64
        self.ecParams = domain_params.dump()
1✔
65

66
        keyID = (0x01,)
1✔
67
        baseKeyPubTemplate = [
1✔
68
            (PyKCS11.CKA_CLASS, PyKCS11.CKO_PUBLIC_KEY),
69
            (PyKCS11.CKA_PRIVATE, PyKCS11.CK_FALSE),
70
            (PyKCS11.CKA_TOKEN, PyKCS11.CK_TRUE),
71
            (PyKCS11.CKA_ENCRYPT, PyKCS11.CK_TRUE),
72
            (PyKCS11.CKA_VERIFY, PyKCS11.CK_TRUE),
73
            (PyKCS11.CKA_WRAP, PyKCS11.CK_TRUE),
74
            (PyKCS11.CKA_KEY_TYPE, PyKCS11.CKK_ECDSA),
75
            (PyKCS11.CKA_EC_PARAMS, self.ecParams),
76
            (PyKCS11.CKA_LABEL, "TestBaseKeyP256"),
77
            (PyKCS11.CKA_ID, keyID),
78
        ]
79
        baseKeyPvtTemplate = [
1✔
80
            (PyKCS11.CKA_CLASS, PyKCS11.CKO_PRIVATE_KEY),
81
            (PyKCS11.CKA_KEY_TYPE, PyKCS11.CKK_ECDSA),
82
            (PyKCS11.CKA_TOKEN, PyKCS11.CK_TRUE),
83
            (PyKCS11.CKA_SENSITIVE, PyKCS11.CK_TRUE),
84
            (PyKCS11.CKA_PRIVATE, PyKCS11.CK_FALSE),
85
            (PyKCS11.CKA_DECRYPT, PyKCS11.CK_TRUE),
86
            (PyKCS11.CKA_SIGN, PyKCS11.CK_TRUE),
87
            (PyKCS11.CKA_UNWRAP, PyKCS11.CK_TRUE),
88
            (PyKCS11.CKA_LABEL, "TestBaseKeyP256"),
89
            (PyKCS11.CKA_ID, keyID),
90
            (PyKCS11.CKA_DERIVE, PyKCS11.CK_TRUE),
91
        ]
92
        mechanism = PyKCS11.Mechanism(PyKCS11.CKM_EC_KEY_PAIR_GEN, None)
1✔
93
        self.baseEcPubKey, self.baseEcPvtKey = self.session.generateKeyPair(
1✔
94
            baseKeyPubTemplate, baseKeyPvtTemplate, mechanism
95
        )
96
        self.assertIsNotNone(self.baseEcPubKey)
1✔
97
        self.assertIsNotNone(self.baseEcPvtKey)
1✔
98

99
    def tearDown(self):
1✔
100
        self.session.destroyObject(self.baseEcPubKey)
1✔
101
        self.session.destroyObject(self.baseEcPvtKey)
1✔
102

103
        self.session.logout()
1✔
104
        self.pkcs11.closeAllSessions(self.slot)
1✔
105
        self.pkcs11.unload()
1✔
106
        del self.pkcs11
1✔
107

108
    def getCkaValue(self, key):
1✔
109
        return list(self.session.getAttributeValue(key, [PyKCS11.CKA_VALUE])[0])
×
110

111
    def test_deriveKey_ECDH1_DERIVE(self):
1✔
112
        if self.SoftHSMversion[0] < 2:
1✔
113
            self.skipTest("generateKeyPair() only supported by SoftHSM >= 2")
×
114

115
        keyID = (0x11,)
1✔
116
        pubTemplate = [
1✔
117
            (PyKCS11.CKA_CLASS, PyKCS11.CKO_PUBLIC_KEY),
118
            (PyKCS11.CKA_PRIVATE, PyKCS11.CK_FALSE),
119
            (PyKCS11.CKA_TOKEN, PyKCS11.CK_FALSE),
120
            (PyKCS11.CKA_ENCRYPT, PyKCS11.CK_TRUE),
121
            (PyKCS11.CKA_VERIFY, PyKCS11.CK_TRUE),
122
            (PyKCS11.CKA_WRAP, PyKCS11.CK_TRUE),
123
            (PyKCS11.CKA_KEY_TYPE, PyKCS11.CKK_ECDSA),
124
            (PyKCS11.CKA_EC_PARAMS, self.ecParams),
125
            (PyKCS11.CKA_LABEL, "testKeyP256"),
126
            (PyKCS11.CKA_ID, keyID),
127
        ]
128
        pvtTemplate = [
1✔
129
            (PyKCS11.CKA_CLASS, PyKCS11.CKO_PRIVATE_KEY),
130
            (PyKCS11.CKA_KEY_TYPE, PyKCS11.CKK_ECDSA),
131
            (PyKCS11.CKA_TOKEN, PyKCS11.CK_FALSE),
132
            (PyKCS11.CKA_SENSITIVE, PyKCS11.CK_TRUE),
133
            (PyKCS11.CKA_PRIVATE, PyKCS11.CK_TRUE),
134
            (PyKCS11.CKA_DECRYPT, PyKCS11.CK_TRUE),
135
            (PyKCS11.CKA_SIGN, PyKCS11.CK_TRUE),
136
            (PyKCS11.CKA_UNWRAP, PyKCS11.CK_TRUE),
137
            (PyKCS11.CKA_LABEL, "testKeyP256"),
138
            (PyKCS11.CKA_ID, keyID),
139
            (PyKCS11.CKA_DERIVE, PyKCS11.CK_TRUE),
140
        ]
141
        mechanism = PyKCS11.Mechanism(PyKCS11.CKM_EC_KEY_PAIR_GEN, None)
1✔
142
        pubKey, pvtKey = self.session.generateKeyPair(
1✔
143
            pubTemplate, pvtTemplate, mechanism
144
        )
145
        self.assertIsNotNone(pubKey)
1✔
146
        self.assertIsNotNone(pvtKey)
1✔
147

148
        keyID = (0x22,)
1✔
149
        derivedAESKeyTemplate = [
1✔
150
            (PyKCS11.CKA_CLASS, PyKCS11.CKO_SECRET_KEY),
151
            (PyKCS11.CKA_KEY_TYPE, PyKCS11.CKK_AES),
152
            (PyKCS11.CKA_TOKEN, PyKCS11.CK_FALSE),
153
            (PyKCS11.CKA_SENSITIVE, PyKCS11.CK_TRUE),
154
            (PyKCS11.CKA_PRIVATE, PyKCS11.CK_TRUE),
155
            (PyKCS11.CKA_ENCRYPT, PyKCS11.CK_TRUE),
156
            (PyKCS11.CKA_DECRYPT, PyKCS11.CK_TRUE),
157
            (PyKCS11.CKA_SIGN, PyKCS11.CK_FALSE),
158
            (PyKCS11.CKA_EXTRACTABLE, PyKCS11.CK_TRUE),
159
            (PyKCS11.CKA_VERIFY, PyKCS11.CK_FALSE),
160
            (PyKCS11.CKA_VALUE_LEN, 24),
161
            (PyKCS11.CKA_LABEL, "derivedAESKey"),
162
            (PyKCS11.CKA_ID, keyID),
163
        ]
164

165
        # derive key 1 : self.basePvtKey + pubKey
166
        attrs = self.session.getAttributeValue(pubKey, [PyKCS11.CKA_EC_POINT], True)
1✔
167
        mechanism = PyKCS11.ECDH1_DERIVE_Mechanism(bytes(attrs[0]))
1✔
168
        derivedKey = self.session.deriveKey(
1✔
169
            self.baseEcPvtKey, derivedAESKeyTemplate, mechanism
170
        )
171
        self.assertIsNotNone(derivedKey)
1✔
172

173
        # derive key 2 : pvtKey + self.basePubKey
174
        attrs = self.session.getAttributeValue(
1✔
175
            self.baseEcPubKey, [PyKCS11.CKA_EC_POINT], True
176
        )
177
        mechanism = PyKCS11.ECDH1_DERIVE_Mechanism(bytes(attrs[0]))
1✔
178
        derivedKey2 = self.session.deriveKey(pvtKey, derivedAESKeyTemplate, mechanism)
1✔
179
        self.assertIsNotNone(derivedKey2)
1✔
180

181
        DataIn = b"Sample data to test ecdh1 derive"
1✔
182
        mechanism = PyKCS11.Mechanism(PyKCS11.CKM_AES_CBC, "1234567812345678")
1✔
183
        DataOut = self.session.encrypt(derivedKey, DataIn, mechanism)
1✔
184
        DataCheck = self.session.decrypt(derivedKey2, DataOut, mechanism)
1✔
185

186
        # match check values
187
        self.assertSequenceEqual(DataIn, DataCheck)
1✔
188

189
        # cleanup
190
        self.session.destroyObject(derivedKey)
1✔
191
        self.session.destroyObject(derivedKey2)
1✔
192
        self.session.destroyObject(pubKey)
1✔
193
        self.session.destroyObject(pvtKey)
1✔
194

195
    def test_deriveKey_CKM_CONCATENATE_BASE_AND_KEY(self):
1✔
196
        # This mechanism is not supported in the current release of
197
        # SoftHSM (2.6.1), however available in develop branch,
198
        # see https://github.com/opendnssec/SoftHSMv2/commit/fa595c07a185656382c18ea2a6a12cad825d48b4
199
        if self.SoftHSMversion <= (2, 6):
1✔
200
            self.skipTest(
1✔
201
                "CKM_CONCATENATE_BASE_AND_KEY is not supported by SoftHSM <= 2.6"
202
            )
203

204
        # generate a key to concatenate with
205
        keyID = (0x11,)
×
206
        concatenateKeyTemplate = self.aesKeyTemplate + [(PyKCS11.CKA_ID, keyID)]
×
207
        key_gen_mechanism = PyKCS11.Mechanism(PyKCS11.CKM_AES_KEY_GEN, None)
×
208
        concKey = self.session.generateKey(concatenateKeyTemplate, key_gen_mechanism)
×
209
        self.assertIsNotNone(concKey)
×
210

211
        # concatenate two keys
212
        keyID = (0x22,)
×
213
        derivedKeyTemplate = self.genericKeyTemplate + [
×
214
            (PyKCS11.CKA_VALUE_LEN, 64),
215
            (PyKCS11.CKA_ID, keyID),
216
        ]
217
        mechanism = PyKCS11.CONCATENATE_BASE_AND_KEY_Mechanism(concKey)
×
218
        derivedKey = self.session.deriveKey(self.baseKey, derivedKeyTemplate, mechanism)
×
219
        self.assertIsNotNone(derivedKey)
×
220

221
        # check derived key's value
222
        baseKeyValue = self.getCkaValue(self.baseKey)
×
223
        concKeyValue = self.getCkaValue(concKey)
×
224
        derivedKeyValue = self.getCkaValue(derivedKey)
×
225

226
        # match: check values
227
        self.assertSequenceEqual(baseKeyValue + concKeyValue, derivedKeyValue)
×
228

229
        # check that mechanism shares ownership of temporary key
230
        mechanism = PyKCS11.CONCATENATE_BASE_AND_KEY_Mechanism(
×
231
            self.session.generateKey(concatenateKeyTemplate, key_gen_mechanism)
232
        )
233
        derivedKey = self.session.deriveKey(self.baseKey, derivedKeyTemplate, mechanism)
×
234
        self.assertIsNotNone(derivedKey)
×
235

236
        # cleanup
237
        self.session.destroyObject(derivedKey)
×
238
        self.session.destroyObject(concKey)
×
239

240
    def test_deriveKey_CKM_CONCATENATE_BASE_AND_DATA(self):
1✔
241
        # This mechanism is not supported in the current release of
242
        # SoftHSM (2.6.1), however available in develop branch,
243
        # see https://github.com/opendnssec/SoftHSMv2/commit/dba00d73e1b69f65b68397d235e7f73bbf59ab6a
244
        if self.SoftHSMversion <= (2, 6):
1✔
245
            self.skipTest(
1✔
246
                "CKM_CONCATENATE_BASE_AND_DATA is not supported by SoftHSM <= 2.6"
247
            )
248

249
        # generate data to concatenate with
250
        concData = list(self.session.generateRandom(32))
×
251

252
        # concatenate key with data
253
        keyID = (0x22,)
×
254
        derivedKeyTemplate = self.genericKeyTemplate + [
×
255
            (PyKCS11.CKA_VALUE_LEN, 64),
256
            (PyKCS11.CKA_ID, keyID),
257
        ]
258
        mechanism = PyKCS11.CONCATENATE_BASE_AND_DATA_Mechanism(concData)
×
259
        derivedKey = self.session.deriveKey(self.baseKey, derivedKeyTemplate, mechanism)
×
260
        self.assertIsNotNone(derivedKey)
×
261

262
        # check derived key's value
263
        baseKeyValue = self.getCkaValue(self.baseKey)
×
264
        derivedKeyValue = self.getCkaValue(derivedKey)
×
265

266
        # match: check values
267
        self.assertSequenceEqual(baseKeyValue + concData, derivedKeyValue)
×
268

269
        # cleanup
270
        self.session.destroyObject(derivedKey)
×
271

272
    def test_deriveKey_CKM_CONCATENATE_DATA_AND_BASE(self):
1✔
273
        # This mechanism is not supported in the current release of
274
        # SoftHSM (2.6.1), however available in develop branch,
275
        # see https://github.com/opendnssec/SoftHSMv2/commit/fae0d9f769ac30d25f563c5fc6c417e9199e4403
276
        if self.SoftHSMversion <= (2, 6):
1✔
277
            self.skipTest(
1✔
278
                "CKM_CONCATENATE_DATA_AND_BASE is not supported by SoftHSM <= 2.6"
279
            )
280

281
        # generate data to concatenate with
282
        concData = list(self.session.generateRandom(32))
×
283

284
        # concatenate data with key
285
        keyID = (0x22,)
×
286
        derivedKeyTemplate = self.genericKeyTemplate + [
×
287
            (PyKCS11.CKA_VALUE_LEN, 64),
288
            (PyKCS11.CKA_ID, keyID),
289
        ]
290
        mechanism = PyKCS11.CONCATENATE_DATA_AND_BASE_Mechanism(concData)
×
291
        derivedKey = self.session.deriveKey(self.baseKey, derivedKeyTemplate, mechanism)
×
292
        self.assertIsNotNone(derivedKey)
×
293

294
        # check derived key's value
295
        baseKeyValue = self.getCkaValue(self.baseKey)
×
296
        derivedKeyValue = self.getCkaValue(derivedKey)
×
297

298
        # match: check values
299
        self.assertSequenceEqual(concData + baseKeyValue, derivedKeyValue)
×
300

301
        # cleanup
302
        self.session.destroyObject(derivedKey)
×
303

304
    def test_deriveKey_CKM_XOR_BASE_AND_DATA(self):
1✔
305
        if self.manufacturer.startswith("SoftHSM"):
1✔
306
            self.skipTest("SoftHSM does not support CKM_XOR_BASE_AND_DATA")
1✔
307

308
        # generate data to xor with
309
        xorData = list(self.session.generateRandom(32))
×
310

311
        # xor key with data
312
        keyID = (0x22,)
×
313
        derivedKeyTemplate = self.genericKeyTemplate + [
×
314
            (PyKCS11.CKA_VALUE_LEN, 32),
315
            (PyKCS11.CKA_ID, keyID),
316
        ]
317
        mechanism = PyKCS11.XOR_BASE_AND_DATA_Mechanism(xorData)
×
318
        derivedKey = self.session.deriveKey(self.baseKey, derivedKeyTemplate, mechanism)
×
319
        self.assertIsNotNone(derivedKey)
×
320

321
        # check derived key's value
322
        baseKeyValue = self.getCkaValue(self.baseKey)
×
323
        derivedKeyValue = self.getCkaValue(derivedKey)
×
324
        expectedValue = map(lambda x, y: x ^ y, baseKeyValue, xorData)
×
325

326
        # match: check values
327
        self.assertSequenceEqual(list(expectedValue), derivedKeyValue)
×
328

329
        # cleanup
330
        self.session.destroyObject(derivedKey)
×
331

332
    def test_deriveKey_CKM_EXTRACT_KEY_FROM_KEY(self):
1✔
333
        if self.manufacturer.startswith("SoftHSM"):
1✔
334
            self.skipTest("SoftHSM does not support CKM_EXTRACT_KEY_FROM_KEY")
1✔
335

336
        # sample from the PKCS#11 specification 3.0, section 2.43.7
337
        # see https://docs.oasis-open.org/pkcs11/pkcs11-curr/v3.0/os/pkcs11-curr-v3.0-os.html#_Toc30061466
338
        #
339
        # We give an example of how this mechanism works.  Suppose a
340
        # token has a secret key with the 4-byte value 0x329F84A9.  We
341
        # will derive a 2-byte secret key from this key, starting at bit
342
        # position 21 (i.e., the value of the parameter to the
343
        # CKM_EXTRACT_KEY_FROM_KEY mechanism is 21).
344
        # 1.     We write the key’s value in binary: 0011 0010 1001 1111
345
        #    1000 0100 1010 1001.  We regard this binary string as
346
        #    holding the 32 bits of the key, labeled as b0, b1, …, b31.
347
        # 2.     We then extract 16 consecutive bits (i.e., 2 bytes)
348
        #    from this binary string, starting at bit b21.  We obtain
349
        #    the binary string 1001 0101 0010 0110.
350
        # 3.     The value of the new key is thus 0x9526.
351

352
        baseKeyTemplate = self.genericKeyTemplate + [
×
353
            (PyKCS11.CKA_DERIVE, PyKCS11.CK_TRUE),
354
            (PyKCS11.CKA_VALUE, [0x32, 0x9F, 0x84, 0xA9]),
355
        ]
356

357
        # create a base key
358
        baseKey = self.session.createObject(baseKeyTemplate)
×
359

360
        expectedDerivedKeyValue = [0x95, 0x26]
×
361

362
        derivedKeyTemplate = self.genericKeyTemplate + [
×
363
            (PyKCS11.CKA_VALUE_LEN, len(expectedDerivedKeyValue))
364
        ]
365

366
        # extract bytes from the base key
367
        mechanism = PyKCS11.EXTRACT_KEY_FROM_KEY_Mechanism(21)
×
368
        derivedKey = self.session.deriveKey(baseKey, derivedKeyTemplate, mechanism)
×
369
        self.assertIsNotNone(derivedKey)
×
370

371
        derivedKeyValue = self.getCkaValue(derivedKey)
×
372

373
        # match: check derived key value
374
        self.assertSequenceEqual(expectedDerivedKeyValue, derivedKeyValue)
×
375

376
        self.session.destroyObject(baseKey)
×
377
        self.session.destroyObject(derivedKey)
×
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc