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

LudovicRousseau / PyKCS11 / 12855273166

19 Jan 2025 04:26PM UTC coverage: 86.678% (-0.008%) from 86.686%
12855273166

push

github

LudovicRousseau
test_derive: document test_deriveKey_CKM_EXTRACT_KEY_FROM_KEY

2993 of 3453 relevant lines covered (86.68%)

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

3
from asn1crypto.keys import ECDomainParameters, NamedCurve
1✔
4

5
from PyKCS11 import PyKCS11
1✔
6

7

8
class TestUtil(unittest.TestCase):
1✔
9
    def setUp(self):
1✔
10
        self.pkcs11 = PyKCS11.PyKCS11Lib()
1✔
11
        self.pkcs11.load()
1✔
12

13
        # get SoftHSM major version
14
        info = self.pkcs11.getInfo()
1✔
15
        self.SoftHSMversion = info.libraryVersion
1✔
16
        self.manufacturer = info.manufacturerID
1✔
17

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

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

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

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

54
        # Select the curve to be used for the keys
55
        curve = "secp256r1"
1✔
56

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

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

95
    def tearDown(self):
1✔
96
        self.session.destroyObject(self.baseEcPubKey)
1✔
97
        self.session.destroyObject(self.baseEcPvtKey)
1✔
98

99
        self.session.logout()
1✔
100
        self.pkcs11.closeAllSessions(self.slot)
1✔
101
        self.pkcs11.unload()
1✔
102
        del self.pkcs11
1✔
103

104
    def getCkaValue(self, key):
1✔
105
        return list(self.session.getAttributeValue(key, [PyKCS11.CKA_VALUE])[0])
×
106

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

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

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

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

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

177
        DataIn = b"Sample data to test ecdh1 derive"
1✔
178
        mechanism = PyKCS11.Mechanism(PyKCS11.CKM_AES_CBC, "1234567812345678")
1✔
179
        DataOut = self.session.encrypt(derivedKey, DataIn, mechanism)
1✔
180
        DataCheck = self.session.decrypt(derivedKey2, DataOut, mechanism)
1✔
181

182
        # match check values
183
        self.assertSequenceEqual(DataIn, DataCheck)
1✔
184

185
        # cleanup
186
        self.session.destroyObject(derivedKey)
1✔
187
        self.session.destroyObject(derivedKey2)
1✔
188
        self.session.destroyObject(pubKey)
1✔
189
        self.session.destroyObject(pvtKey)
1✔
190

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

199
        # generate a key to concatenate with
200
        keyID = (0x11,)
×
201
        concatenateKeyTemplate = self.aesKeyTemplate + [(PyKCS11.CKA_ID, keyID)]
×
202
        mechanism = PyKCS11.Mechanism(PyKCS11.CKM_AES_KEY_GEN, None)
×
203
        concKey = self.session.generateKey(concatenateKeyTemplate, mechanism)
×
204
        self.assertIsNotNone(concKey)
×
205

206
        # concatenate two keys
207
        keyID = (0x22,)
×
208
        derivedKeyTemplate = self.genericKeyTemplate + [
×
209
            (PyKCS11.CKA_VALUE_LEN, 64),
210
            (PyKCS11.CKA_ID, keyID),
211
        ]
212
        mechanism = PyKCS11.CONCATENATE_BASE_AND_KEY_Mechanism(concKey)
×
213
        derivedKey = self.session.deriveKey(self.baseKey, derivedKeyTemplate, mechanism)
×
214
        self.assertIsNotNone(derivedKey)
×
215

216
        # check derived key's value
217
        baseKeyValue = self.getCkaValue(self.baseKey)
×
218
        concKeyValue = self.getCkaValue(concKey)
×
219
        derivedKeyValue = self.getCkaValue(derivedKey)
×
220

221
        # match: check values
222
        self.assertSequenceEqual(baseKeyValue + concKeyValue, derivedKeyValue)
×
223

224
        # cleanup
225
        self.session.destroyObject(derivedKey)
×
226
        self.session.destroyObject(concKey)
×
227

228
    def test_deriveKey_CKM_CONCATENATE_BASE_AND_DATA(self):
1✔
229
        # This mechanism is not supported in the current release of SoftHSM (2.6.1), however available in develop branch,
230
        # see https://github.com/opendnssec/SoftHSMv2/commit/dba00d73e1b69f65b68397d235e7f73bbf59ab6a
231
        if self.SoftHSMversion <= (2, 6):
1✔
232
            self.skipTest(
1✔
233
                "CKM_CONCATENATE_BASE_AND_DATA is not supported by SoftHSM <= 2.6"
234
            )
235

236
        # generate data to concatenate with
237
        concData = list(self.session.generateRandom(32))
×
238

239
        # concatenate key with data
240
        keyID = (0x22,)
×
241
        derivedKeyTemplate = self.genericKeyTemplate + [
×
242
            (PyKCS11.CKA_VALUE_LEN, 64),
243
            (PyKCS11.CKA_ID, keyID),
244
        ]
245
        mechanism = PyKCS11.CONCATENATE_BASE_AND_DATA_Mechanism(concData)
×
246
        derivedKey = self.session.deriveKey(self.baseKey, derivedKeyTemplate, mechanism)
×
247
        self.assertIsNotNone(derivedKey)
×
248

249
        # check derived key's value
250
        baseKeyValue = self.getCkaValue(self.baseKey)
×
251
        derivedKeyValue = self.getCkaValue(derivedKey)
×
252

253
        # match: check values
254
        self.assertSequenceEqual(baseKeyValue + concData, derivedKeyValue)
×
255

256
        # cleanup
257
        self.session.destroyObject(derivedKey)
×
258

259
    def test_deriveKey_CKM_CONCATENATE_DATA_AND_BASE(self):
1✔
260
        # This mechanism is not supported in the current release of SoftHSM (2.6.1), however available in develop branch,
261
        # see https://github.com/opendnssec/SoftHSMv2/commit/fae0d9f769ac30d25f563c5fc6c417e9199e4403
262
        if self.SoftHSMversion <= (2, 6):
1✔
263
            self.skipTest(
1✔
264
                "CKM_CONCATENATE_DATA_AND_BASE is not supported by SoftHSM <= 2.6"
265
            )
266

267
        # generate data to concatenate with
268
        concData = list(self.session.generateRandom(32))
×
269

270
        # concatenate data with key
271
        keyID = (0x22,)
×
272
        derivedKeyTemplate = self.genericKeyTemplate + [
×
273
            (PyKCS11.CKA_VALUE_LEN, 64),
274
            (PyKCS11.CKA_ID, keyID),
275
        ]
276
        mechanism = PyKCS11.CONCATENATE_DATA_AND_BASE_Mechanism(concData)
×
277
        derivedKey = self.session.deriveKey(self.baseKey, derivedKeyTemplate, mechanism)
×
278
        self.assertIsNotNone(derivedKey)
×
279

280
        # check derived key's value
281
        baseKeyValue = self.getCkaValue(self.baseKey)
×
282
        derivedKeyValue = self.getCkaValue(derivedKey)
×
283

284
        # match: check values
285
        self.assertSequenceEqual(concData + baseKeyValue, derivedKeyValue)
×
286

287
        # cleanup
288
        self.session.destroyObject(derivedKey)
×
289

290
    def test_deriveKey_CKM_XOR_BASE_AND_DATA(self):
1✔
291
        if self.manufacturer.startswith("SoftHSM"):
1✔
292
            self.skipTest("SoftHSM does not support CKM_XOR_BASE_AND_DATA")
1✔
293

294
        # generate data to xor with
295
        xorData = list(self.session.generateRandom(32))
×
296

297
        # xor key with data
298
        keyID = (0x22,)
×
299
        derivedKeyTemplate = self.genericKeyTemplate + [
×
300
            (PyKCS11.CKA_VALUE_LEN, 32),
301
            (PyKCS11.CKA_ID, keyID),
302
        ]
303
        mechanism = PyKCS11.XOR_BASE_AND_DATA_Mechanism(xorData)
×
304
        derivedKey = self.session.deriveKey(self.baseKey, derivedKeyTemplate, mechanism)
×
305
        self.assertIsNotNone(derivedKey)
×
306

307
        # check derived key's value
308
        baseKeyValue = self.getCkaValue(self.baseKey)
×
309
        derivedKeyValue = self.getCkaValue(derivedKey)
×
310
        expectedValue = map(lambda x, y: x ^ y, baseKeyValue, xorData)
×
311

312
        # match: check values
313
        self.assertSequenceEqual(list(expectedValue), derivedKeyValue)
×
314

315
        # cleanup
316
        self.session.destroyObject(derivedKey)
×
317

318
    def test_deriveKey_CKM_EXTRACT_KEY_FROM_KEY(self):
1✔
319
        if self.manufacturer.startswith("SoftHSM"):
1✔
320
            self.skipTest("SoftHSM does not support CKM_EXTRACT_KEY_FROM_KEY")
1✔
321

322
        # sample from the PKCS#11 specification 3.0, section 2.43.7
323
        # see https://docs.oasis-open.org/pkcs11/pkcs11-curr/v3.0/os/pkcs11-curr-v3.0-os.html#_Toc30061466
324
        #
325
        # We give an example of how this mechanism works.  Suppose a
326
        # token has a secret key with the 4-byte value 0x329F84A9.  We
327
        # will derive a 2-byte secret key from this key, starting at bit
328
        # position 21 (i.e., the value of the parameter to the
329
        # CKM_EXTRACT_KEY_FROM_KEY mechanism is 21).
330
        # 1.     We write the key’s value in binary: 0011 0010 1001 1111
331
        #    1000 0100 1010 1001.  We regard this binary string as
332
        #    holding the 32 bits of the key, labeled as b0, b1, …, b31.
333
        # 2.     We then extract 16 consecutive bits (i.e., 2 bytes)
334
        #    from this binary string, starting at bit b21.  We obtain
335
        #    the binary string 1001 0101 0010 0110.
336
        # 3.     The value of the new key is thus 0x9526.
337

338
        baseKeyTemplate = self.genericKeyTemplate + [
×
339
            (PyKCS11.CKA_DERIVE, PyKCS11.CK_TRUE),
340
            (PyKCS11.CKA_VALUE, [0x32, 0x9F, 0x84, 0xA9]),
341
        ]
342

343
        # create a base key
344
        baseKey = self.session.createObject(baseKeyTemplate)
×
345

346
        expectedDerivedKeyValue = [0x95, 0x26]
×
347

348
        derivedKeyTemplate = self.genericKeyTemplate + [
×
349
            (PyKCS11.CKA_VALUE_LEN, len(expectedDerivedKeyValue))
350
        ]
351

352
        # extract bytes from the base key
353
        mechanism = PyKCS11.EXTRACT_KEY_FROM_KEY_Mechanism(21)
×
354
        derivedKey = self.session.deriveKey(baseKey, derivedKeyTemplate, mechanism)
×
355
        self.assertIsNotNone(derivedKey)
×
356

357
        derivedKeyValue = self.getCkaValue(derivedKey)
×
358

359
        # match: check derived key value
360
        self.assertSequenceEqual(expectedDerivedKeyValue, derivedKeyValue)
×
361

362
        self.session.destroyObject(baseKey)
×
363
        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