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

LudovicRousseau / PyKCS11 / 12851805829

19 Jan 2025 08:20AM UTC coverage: 86.686% (-0.5%) from 87.2%
12851805829

Pull #128

github

web-flow
Merge 10903b7dc into 6674d1e04
Pull Request #128: add CKM_EXTRACT_KEY_FROM_KEY mechanism

6 of 24 new or added lines in 2 files covered. (25.0%)

16 existing lines in 1 file now uncovered.

2995 of 3455 relevant lines covered (86.69%)

0.87 hits per line

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

57.45
/test/test_derive.py
1
import unittest
1✔
2
from asn1crypto.keys import ECDomainParameters, NamedCurve
1✔
3
from PyKCS11 import PyKCS11
1✔
4

5
class TestUtil(unittest.TestCase):
1✔
6
    def setUp(self):
1✔
7
        self.pkcs11 = PyKCS11.PyKCS11Lib()
1✔
8
        self.pkcs11.load()
1✔
9

10
        # get SoftHSM major version
11
        info = self.pkcs11.getInfo()
1✔
12
        self.SoftHSMversion = info.libraryVersion
1✔
13
        self.manufacturer = info.manufacturerID
1✔
14

15
        self.slot = self.pkcs11.getSlotList(tokenPresent=True)[0]
1✔
16
        self.session = self.pkcs11.openSession(
1✔
17
            self.slot, PyKCS11.CKF_SERIAL_SESSION | PyKCS11.CKF_RW_SESSION
18
        )
19
        self.session.login("1234")
1✔
20

21
        # common templates used in derive test cases
22
        self.aesKeyTemplate = [
1✔
23
            (PyKCS11.CKA_CLASS, PyKCS11.CKO_SECRET_KEY),
24
            (PyKCS11.CKA_KEY_TYPE, PyKCS11.CKK_AES),
25
            (PyKCS11.CKA_VALUE_LEN, 32),
26
            (PyKCS11.CKA_TOKEN, PyKCS11.CK_TRUE),
27
            (PyKCS11.CKA_SENSITIVE, PyKCS11.CK_FALSE),
28
            (PyKCS11.CKA_EXTRACTABLE, PyKCS11.CK_TRUE),
29
            (PyKCS11.CKA_PRIVATE, PyKCS11.CK_TRUE),
30
            (PyKCS11.CKA_LABEL, "DeriveTestBaseAes256Key"),
31
            (PyKCS11.CKA_DERIVE, PyKCS11.CK_TRUE),
32
        ]
33

34
        self.genericKeyTemplate = [
1✔
35
            (PyKCS11.CKA_CLASS, PyKCS11.CKO_SECRET_KEY),
36
            (PyKCS11.CKA_KEY_TYPE, PyKCS11.CKK_GENERIC_SECRET),
37
            (PyKCS11.CKA_TOKEN, PyKCS11.CK_FALSE),
38
            (PyKCS11.CKA_SENSITIVE, PyKCS11.CK_FALSE),
39
            (PyKCS11.CKA_EXTRACTABLE, PyKCS11.CK_TRUE),
40
            (PyKCS11.CKA_PRIVATE, PyKCS11.CK_TRUE),
41
            (PyKCS11.CKA_LABEL, "DeriveTestGenericKey"),
42
        ]
43

44
        # generate a common symmetric base key for tests
45
        keyID = (0x01,)
1✔
46
        baseKeyTemplate = self.aesKeyTemplate + [(PyKCS11.CKA_ID, keyID)]
1✔
47
        mechanism = PyKCS11.Mechanism(PyKCS11.CKM_AES_KEY_GEN, None)
1✔
48
        self.baseKey = self.session.generateKey(baseKeyTemplate, mechanism)
1✔
49
        self.assertIsNotNone(self.baseKey)
1✔
50

51
        # Select the curve to be used for the keys
52
        curve = u"secp256r1"
1✔
53

54
        # Setup the domain parameters, unicode conversion needed
55
        # for the curve string
56
        domain_params = ECDomainParameters(name="named", value=NamedCurve(curve))
1✔
57
        self.ecParams = domain_params.dump()
1✔
58

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

90
    def tearDown(self):
1✔
91
        self.session.destroyObject(self.baseEcPubKey)
1✔
92
        self.session.destroyObject(self.baseEcPvtKey)
1✔
93

94
        self.session.logout()
1✔
95
        self.pkcs11.closeAllSessions(self.slot)
1✔
96
        self.pkcs11.unload()
1✔
97
        del self.pkcs11
1✔
98

99
    def getCkaValue(self, key):
1✔
100
        return list(
×
101
            self.session.getAttributeValue(
102
            key, [PyKCS11.CKA_VALUE])[0]
103
        )
104

105
    def test_deriveKey_ECDH1_DERIVE(self):
1✔
106
        if self.SoftHSMversion[0] < 2:
1✔
107
            self.skipTest("generateKeyPair() only supported by SoftHSM >= 2")
×
108

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

140
        keyID = (0x22,)
1✔
141
        derivedAESKeyTemplate = [
1✔
142
            (PyKCS11.CKA_CLASS, PyKCS11.CKO_SECRET_KEY),
143
            (PyKCS11.CKA_KEY_TYPE, PyKCS11.CKK_AES),
144
            (PyKCS11.CKA_TOKEN, PyKCS11.CK_FALSE),
145
            (PyKCS11.CKA_SENSITIVE, PyKCS11.CK_TRUE),
146
            (PyKCS11.CKA_PRIVATE, PyKCS11.CK_TRUE),
147
            (PyKCS11.CKA_ENCRYPT, PyKCS11.CK_TRUE),
148
            (PyKCS11.CKA_DECRYPT, PyKCS11.CK_TRUE),
149
            (PyKCS11.CKA_SIGN, PyKCS11.CK_FALSE),
150
            (PyKCS11.CKA_EXTRACTABLE, PyKCS11.CK_TRUE),
151
            (PyKCS11.CKA_VERIFY, PyKCS11.CK_FALSE),
152
            (PyKCS11.CKA_VALUE_LEN, 24),
153
            (PyKCS11.CKA_LABEL, "derivedAESKey"),
154
            (PyKCS11.CKA_ID, keyID),
155
        ]
156

157
        # derive key 1 : self.basePvtKey + pubKey
158
        attrs = self.session.getAttributeValue(pubKey, [PyKCS11.CKA_EC_POINT], True)
1✔
159
        mechanism = PyKCS11.ECDH1_DERIVE_Mechanism(bytes(attrs[0]))
1✔
160
        derivedKey = self.session.deriveKey(self.baseEcPvtKey, derivedAESKeyTemplate, mechanism)
1✔
161
        self.assertIsNotNone(derivedKey)
1✔
162

163
        # derive key 2 : pvtKey + self.basePubKey
164
        attrs = self.session.getAttributeValue(self.baseEcPubKey, [PyKCS11.CKA_EC_POINT], True)
1✔
165
        mechanism = PyKCS11.ECDH1_DERIVE_Mechanism(bytes(attrs[0]))
1✔
166
        derivedKey2 = self.session.deriveKey(pvtKey, derivedAESKeyTemplate, mechanism)
1✔
167
        self.assertIsNotNone(derivedKey2)
1✔
168

169
        DataIn = "Sample data to test ecdh1 derive".encode("utf-8")
1✔
170
        mechanism = PyKCS11.Mechanism(PyKCS11.CKM_AES_CBC, "1234567812345678")
1✔
171
        DataOut = self.session.encrypt(derivedKey, DataIn, mechanism)
1✔
172
        DataCheck = self.session.decrypt(derivedKey2, DataOut, mechanism)
1✔
173

174
        # match check values
175
        self.assertSequenceEqual(DataIn, DataCheck)
1✔
176

177
        # cleanup
178
        self.session.destroyObject(derivedKey)
1✔
179
        self.session.destroyObject(derivedKey2)
1✔
180
        self.session.destroyObject(pubKey)
1✔
181
        self.session.destroyObject(pvtKey)
1✔
182

183
    def test_deriveKey_CKM_CONCATENATE_BASE_AND_KEY(self):
1✔
184
        # This mechanism is not supported in the current release of SoftHSM (2.6.1), however available in develop branch,
185
        # see https://github.com/opendnssec/SoftHSMv2/commit/fa595c07a185656382c18ea2a6a12cad825d48b4
186
        if self.SoftHSMversion <= (2,6):
1✔
187
            self.skipTest("CKM_CONCATENATE_BASE_AND_KEY is not supported by SoftHSM <= 2.6")
1✔
188

189
        # generate a key to concatenate with
190
        keyID = (0x11,)
×
191
        concatenateKeyTemplate = self.aesKeyTemplate + [(PyKCS11.CKA_ID, keyID)]
×
192
        mechanism = PyKCS11.Mechanism(PyKCS11.CKM_AES_KEY_GEN, None)
×
193
        concKey = self.session.generateKey(concatenateKeyTemplate, mechanism)
×
194
        self.assertIsNotNone(concKey)
×
195

196
        # concatenate two keys
197
        keyID = (0x22,)
×
198
        derivedKeyTemplate = self.genericKeyTemplate + [
×
199
            (PyKCS11.CKA_VALUE_LEN, 64),
200
            (PyKCS11.CKA_ID, keyID)
201
        ]
202
        mechanism = PyKCS11.CONCATENATE_BASE_AND_KEY_Mechanism(concKey)
×
203
        derivedKey = self.session.deriveKey(
×
204
            self.baseKey, derivedKeyTemplate, mechanism)
205
        self.assertIsNotNone(derivedKey)
×
206

207
        # check derived key's value
208
        baseKeyValue = self.getCkaValue(self.baseKey)
×
209
        concKeyValue = self.getCkaValue(concKey)
×
210
        derivedKeyValue = self.getCkaValue(derivedKey)
×
211

212
        # match: check values
213
        self.assertSequenceEqual(baseKeyValue + concKeyValue, derivedKeyValue)
×
214

215
        # cleanup
216
        self.session.destroyObject(derivedKey)
×
217
        self.session.destroyObject(concKey)
×
218

219
    def test_deriveKey_CKM_CONCATENATE_BASE_AND_DATA(self):
1✔
220
        # This mechanism is not supported in the current release of SoftHSM (2.6.1), however available in develop branch,
221
        # see https://github.com/opendnssec/SoftHSMv2/commit/dba00d73e1b69f65b68397d235e7f73bbf59ab6a
222
        if self.SoftHSMversion <= (2,6):
1✔
223
            self.skipTest("CKM_CONCATENATE_BASE_AND_DATA is not supported by SoftHSM <= 2.6")
1✔
224

225
        # generate data to concatenate with
226
        concData = list(self.session.generateRandom(32))
×
227

228
        # concatenate key with data
229
        keyID = (0x22,)
×
230
        derivedKeyTemplate = self.genericKeyTemplate + [
×
231
            (PyKCS11.CKA_VALUE_LEN, 64),
232
            (PyKCS11.CKA_ID, keyID)
233
        ]
234
        mechanism = PyKCS11.CONCATENATE_BASE_AND_DATA_Mechanism(concData)
×
235
        derivedKey = self.session.deriveKey(
×
236
            self.baseKey, derivedKeyTemplate, mechanism)
237
        self.assertIsNotNone(derivedKey)
×
238

239
        # check derived key's value
240
        baseKeyValue = self.getCkaValue(self.baseKey)
×
241
        derivedKeyValue = self.getCkaValue(derivedKey)
×
242

243
        # match: check values
244
        self.assertSequenceEqual(baseKeyValue + concData, derivedKeyValue)
×
245

246
        # cleanup
247
        self.session.destroyObject(derivedKey)
×
248

249
    def test_deriveKey_CKM_CONCATENATE_DATA_AND_BASE(self):
1✔
250
        # This mechanism is not supported in the current release of SoftHSM (2.6.1), however available in develop branch,
251
        # see https://github.com/opendnssec/SoftHSMv2/commit/fae0d9f769ac30d25f563c5fc6c417e9199e4403
252
        if self.SoftHSMversion <= (2,6):
1✔
253
            self.skipTest("CKM_CONCATENATE_DATA_AND_BASE is not supported by SoftHSM <= 2.6")
1✔
254

255
        # generate data to concatenate with
256
        concData = list(self.session.generateRandom(32))
×
257

258
        # concatenate data with key
259
        keyID = (0x22,)
×
260
        derivedKeyTemplate = self.genericKeyTemplate + [
×
261
            (PyKCS11.CKA_VALUE_LEN, 64),
262
            (PyKCS11.CKA_ID, keyID)
263
        ]
264
        mechanism = PyKCS11.CONCATENATE_DATA_AND_BASE_Mechanism(concData)
×
265
        derivedKey = self.session.deriveKey(
×
266
            self.baseKey, derivedKeyTemplate, mechanism)
267
        self.assertIsNotNone(derivedKey)
×
268

269
        # check derived key's value
270
        baseKeyValue = self.getCkaValue(self.baseKey)
×
271
        derivedKeyValue = self.getCkaValue(derivedKey)
×
272

273
        # match: check values
274
        self.assertSequenceEqual(concData + baseKeyValue, derivedKeyValue)
×
275

276
        # cleanup
277
        self.session.destroyObject(derivedKey)
×
278

279
    def test_deriveKey_CKM_XOR_BASE_AND_DATA(self):
1✔
280
        if self.manufacturer.startswith("SoftHSM"):
1✔
281
            self.skipTest("SoftHSM does not support CKM_XOR_BASE_AND_DATA")
1✔
282

283
        # generate data to xor with
284
        xorData = list(self.session.generateRandom(32))
×
285

286
        # xor key with data
287
        keyID = (0x22,)
×
288
        derivedKeyTemplate = self.genericKeyTemplate + [
×
289
            (PyKCS11.CKA_VALUE_LEN, 32),
290
            (PyKCS11.CKA_ID, keyID)
291
        ]
292
        mechanism = PyKCS11.XOR_BASE_AND_DATA_Mechanism(xorData)
×
293
        derivedKey = self.session.deriveKey(
×
294
            self.baseKey, derivedKeyTemplate, mechanism)
295
        self.assertIsNotNone(derivedKey)
×
296

297
        # check derived key's value
298
        baseKeyValue = self.getCkaValue(self.baseKey)
×
299
        derivedKeyValue = self.getCkaValue(derivedKey)
×
300
        expectedValue = map(lambda x, y: x ^ y, baseKeyValue, xorData)
×
301

302
        # match: check values
303
        self.assertSequenceEqual(list(expectedValue), derivedKeyValue)
×
304

305
        # cleanup
306
        self.session.destroyObject(derivedKey)
×
307

308
    def test_deriveKey_CKM_EXTRACT_KEY_FROM_KEY(self):
1✔
309
        if self.manufacturer.startswith("SoftHSM"):
1✔
310
            self.skipTest("SoftHSM does not support CKM_EXTRACT_KEY_FROM_KEY")
1✔
311

312
        # sample from the PKCS#11 specification 3.0, section 2.43.7
313
        # see https://docs.oasis-open.org/pkcs11/pkcs11-curr/v3.0/os/pkcs11-curr-v3.0-os.html#_Toc30061466
NEW
314
        baseKeyTemplate = self.genericKeyTemplate + [
×
315
            (PyKCS11.CKA_DERIVE, PyKCS11.CK_TRUE),
316
            (PyKCS11.CKA_VALUE, [0x32, 0x9f, 0x84, 0xa9])]
317

318
        # create a base key
NEW
319
        baseKey = self.session.createObject(baseKeyTemplate)
×
320

NEW
321
        expectedDerivedKeyValue = [0x95, 0x26]
×
322

NEW
323
        derivedKeyTemplate = self.genericKeyTemplate + [(
×
324
            PyKCS11.CKA_VALUE_LEN, len(expectedDerivedKeyValue))]
325
        
326
        # extract bytes from the base key
NEW
327
        mechanism = PyKCS11.EXTRACT_KEY_FROM_KEY_Mechanism(21)
×
NEW
328
        derivedKey = self.session.deriveKey(
×
329
            baseKey, derivedKeyTemplate, mechanism)
NEW
330
        self.assertIsNotNone(derivedKey)
×
331

NEW
332
        derivedKeyValue = self.getCkaValue(derivedKey)
×
333

334
        # match: check derived key value
NEW
335
        self.assertSequenceEqual(expectedDerivedKeyValue, derivedKeyValue)
×
336

NEW
337
        self.session.destroyObject(baseKey)
×
NEW
338
        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