python-pkcs11 0.10.1__cp315-cp315-win_amd64.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
pkcs11/_pkcs11.pyx ADDED
@@ -0,0 +1,2327 @@
1
+ #cython: language_level=3
2
+ #coverage#cython: linetrace=True
3
+ """
4
+ High-level Python PKCS#11 Wrapper.
5
+
6
+ Most class here inherit from pkcs11.types, which provides easier introspection
7
+ for Sphinx/Jedi/etc, as this module is not importable without having the
8
+ library loaded.
9
+ """
10
+
11
+ from __future__ import (absolute_import, unicode_literals,
12
+ print_function, division)
13
+
14
+ from threading import RLock
15
+
16
+ from cpython.mem cimport PyMem_Malloc, PyMem_Free
17
+ from cpython.bytes cimport PyBytes_FromStringAndSize
18
+ from libc.string cimport memcpy
19
+
20
+ from pkcs11 import types
21
+ from pkcs11.attributes import AttributeMapper
22
+ from pkcs11.defaults import *
23
+ from pkcs11.exceptions import *
24
+ from pkcs11.constants import *
25
+ from pkcs11.mechanisms import *
26
+ from pkcs11.types import (
27
+ _CK_UTF8CHAR_to_str,
28
+ _CK_VERSION_to_tuple,
29
+ _CK_MECHANISM_TYPE_to_enum,
30
+ PROTECTED_AUTH,
31
+ )
32
+
33
+
34
+ cdef class lib(HasFuncList)
35
+
36
+ cdef class HasFuncList:
37
+ cdef CK_FUNCTION_LIST *funclist
38
+ cdef CK_FUNCTION_LIST_3_0 *funclist30
39
+ cdef CK_FUNCTION_LIST_3_2 *funclist32
40
+
41
+ def __cinit__(self, *args, **kwargs):
42
+ self.funclist = NULL
43
+ self.funclist30 = NULL
44
+ self.funclist32 = NULL
45
+
46
+
47
+ cdef assertRV(rv) with gil:
48
+ """Check for an acceptable RV value or thrown an exception."""
49
+ if rv == CKR_OK:
50
+ return
51
+ raise map_rv_to_error(rv)
52
+
53
+
54
+ cdef class AttributeList:
55
+ """
56
+ A list of CK_ATTRIBUTE objects.
57
+ """
58
+
59
+ cdef CK_ATTRIBUTE *data
60
+ """CK_ATTRIBUTE * representation of the data."""
61
+ cdef CK_ULONG count
62
+ """Length of `data`."""
63
+
64
+ def __cinit__(self, attrs):
65
+ self.data = NULL
66
+ self.count = 0
67
+
68
+ @staticmethod
69
+ cdef AttributeList from_owned_pointer(CK_ATTRIBUTE * data, CK_ULONG count):
70
+ cdef AttributeList lst = AttributeList.__new__(AttributeList, ())
71
+ lst.data = data
72
+ lst.count = count
73
+ return lst
74
+
75
+ @staticmethod
76
+ cdef AttributeList from_template(dict template, object attribute_mapper):
77
+ cdef AttributeList lst = AttributeList.__new__(AttributeList, ())
78
+
79
+ lst.count = count = <CK_ULONG> len(template)
80
+
81
+ lst.data = <CK_ATTRIBUTE *> PyMem_Malloc(count * sizeof(CK_ATTRIBUTE))
82
+ if lst.data is NULL:
83
+ raise MemoryError()
84
+
85
+ cdef bytes value_bytes
86
+ cdef CK_CHAR * value_ptr
87
+ cdef Py_ssize_t value_len
88
+ for index, (key, value) in enumerate(template.items()):
89
+ lst.data[index].type = key
90
+ value_bytes = attribute_mapper.pack_attribute(key, value)
91
+ value_len = len(value_bytes)
92
+ # copy the result into a pointer that we manage, for consistency with the other init method
93
+ value_ptr = <CK_CHAR *> PyMem_Malloc(value_len)
94
+ if value_ptr is NULL:
95
+ raise MemoryError()
96
+ memcpy(value_ptr, <CK_CHAR *> value_bytes, <size_t> value_len)
97
+ lst.data[index].pValue = <CK_CHAR *> value_ptr
98
+ lst.data[index].ulValueLen = <CK_ULONG> value_len
99
+
100
+ return lst
101
+
102
+ def __init__(self, attrs):
103
+ raise TypeError
104
+
105
+ cdef at_index(self, CK_ULONG index, object attribute_mapper):
106
+ cdef CK_ATTRIBUTE * attr
107
+ if index < self.count:
108
+ attr = &self.data[index]
109
+ return attribute_mapper.unpack_attributes(
110
+ attr.type,
111
+ PyBytes_FromStringAndSize(<char *> attr.pValue, <Py_ssize_t> attr.ulValueLen)
112
+ )
113
+ else:
114
+ raise IndexError()
115
+
116
+ def as_dict(self, attribute_mapper):
117
+ cdef CK_ATTRIBUTE * attr
118
+ result = {}
119
+ for index in range(self.count):
120
+ attr = &self.data[index]
121
+ result[attr.type] = self.at_index(index, attribute_mapper)
122
+ return result
123
+
124
+ def get(self, item, attribute_mapper):
125
+ cdef CK_ULONG index = 0
126
+ cdef CK_ATTRIBUTE_TYPE key = item
127
+ for index in range(self.count):
128
+ if self.data[index].type == key:
129
+ return self.at_index(index, attribute_mapper)
130
+ raise KeyError(item)
131
+
132
+ def __dealloc__(self):
133
+ cdef CK_ULONG index = 0
134
+ if self.data is not NULL:
135
+ for index in range(self.count):
136
+ PyMem_Free(self.data[index].pValue)
137
+ PyMem_Free(self.data)
138
+
139
+
140
+ cdef class MechanismWithParam:
141
+ """
142
+ Python wrapper for a Mechanism with its parameter
143
+ """
144
+
145
+ cdef CK_MECHANISM *data
146
+ """The mechanism."""
147
+ cdef void *param
148
+ """Reference to a pointer we might need to free."""
149
+ cdef object _python_param
150
+ """
151
+ Hold a reference to the original parameter object so it doesn't get
152
+ GC'd before this one.
153
+ """
154
+
155
+ def __cinit__(self, *args):
156
+ self.data = <CK_MECHANISM *> PyMem_Malloc(sizeof(CK_MECHANISM))
157
+ self.param = NULL
158
+ self._python_param = None
159
+
160
+ def __init__(self, key_type, mapping, mechanism=None, param=None):
161
+ self._python_param = param
162
+ if mechanism is None:
163
+ try:
164
+ mechanism = mapping[key_type]
165
+ except KeyError:
166
+ raise ArgumentsBad("No default mechanism for this key type. "
167
+ "Please specify `mechanism`.")
168
+
169
+ if not isinstance(mechanism, Mechanism):
170
+ raise ArgumentsBad("`mechanism` must be a Mechanism.")
171
+ # Possible types of parameters we might need to allocate
172
+ # These are used to make assigning to the object we malloc() easier
173
+ # FIXME: is there a better way to do this?
174
+ cdef CK_RSA_PKCS_OAEP_PARAMS *oaep_params
175
+ cdef CK_RSA_PKCS_PSS_PARAMS *pss_params
176
+ cdef CK_EDDSA_PARAMS *eddsa_params
177
+ cdef CK_ECDH1_DERIVE_PARAMS *ecdh1_params
178
+ cdef CK_KEY_DERIVATION_STRING_DATA *aes_ecb_params
179
+ cdef CK_AES_CBC_ENCRYPT_DATA_PARAMS *aes_cbc_params
180
+ cdef CK_GCM_PARAMS *gcm_params
181
+ cdef CK_AES_CTR_PARAMS *aes_ctr_params
182
+
183
+ # Unpack mechanism parameters
184
+
185
+ if mechanism == Mechanism.AES_ECB_ENCRYPT_DATA:
186
+ paramlen = sizeof(CK_KEY_DERIVATION_STRING_DATA)
187
+ self.param = aes_ecb_params = \
188
+ <CK_KEY_DERIVATION_STRING_DATA *> PyMem_Malloc(paramlen)
189
+ aes_ecb_params.pData = <CK_BYTE *> param
190
+ aes_ecb_params.ulLen = <CK_ULONG> len(param)
191
+
192
+ elif isinstance(param, bytes):
193
+ # Note: this is an escape hatch of sorts that can be used to provide parameters for
194
+ # unsupported algorithms in raw binary form.
195
+ # We include it at this point in the chain for forwards compatibility reasons:
196
+ # if at a later point, "first class" support for the unsupported mechanism is added
197
+ # to the library, existing code that used this "raw mode" workaround will keep working
198
+ # because this branch takes priority.
199
+ #
200
+ # The parameter convention for AES_ECB_ENCRYPT_DATA predates this ordering decision,
201
+ # so it takes precedence over this branch for backwards compatibility.
202
+ self.data.pParameter = <CK_BYTE *> param
203
+ paramlen = len(param)
204
+
205
+ elif mechanism == Mechanism.RSA_PKCS_OAEP:
206
+ paramlen = sizeof(CK_RSA_PKCS_OAEP_PARAMS)
207
+ self.param = oaep_params = \
208
+ <CK_RSA_PKCS_OAEP_PARAMS *> PyMem_Malloc(paramlen)
209
+
210
+ oaep_params.source = CKZ_DATA_SPECIFIED
211
+
212
+ if param is None:
213
+ param = DEFAULT_MECHANISM_PARAMS[mechanism]
214
+
215
+ (oaep_params.hashAlg, oaep_params.mgf, source_data) = param
216
+
217
+ if source_data is None:
218
+ oaep_params.pSourceData = NULL
219
+ oaep_params.ulSourceDataLen = <CK_ULONG> 0
220
+ else:
221
+ oaep_params.pSourceData = <CK_BYTE *> source_data
222
+ oaep_params.ulSourceDataLen = <CK_ULONG> len(source_data)
223
+
224
+ elif mechanism in (Mechanism.RSA_PKCS_PSS,
225
+ Mechanism.SHA1_RSA_PKCS_PSS,
226
+ Mechanism.SHA224_RSA_PKCS_PSS,
227
+ Mechanism.SHA256_RSA_PKCS_PSS,
228
+ Mechanism.SHA384_RSA_PKCS_PSS,
229
+ Mechanism.SHA512_RSA_PKCS_PSS):
230
+ paramlen = sizeof(CK_RSA_PKCS_PSS_PARAMS)
231
+ self.param = pss_params = \
232
+ <CK_RSA_PKCS_PSS_PARAMS *> PyMem_Malloc(paramlen)
233
+
234
+ if param is None:
235
+ # All PSS mechanisms have the same defaults
236
+ param = DEFAULT_MECHANISM_PARAMS[Mechanism.RSA_PKCS_PSS]
237
+
238
+ (pss_params.hashAlg, pss_params.mgf, pss_params.sLen) = param
239
+
240
+ elif mechanism == Mechanism.EDDSA and param is not None:
241
+ paramlen = sizeof(CK_EDDSA_PARAMS)
242
+ self.param = eddsa_params = \
243
+ <CK_EDDSA_PARAMS *> PyMem_Malloc(paramlen)
244
+ (eddsa_params.phFlag, context_data) = param
245
+ if context_data is None:
246
+ eddsa_params.pContextData = NULL
247
+ eddsa_params.ulContextDataLen = 0
248
+ else:
249
+ eddsa_params.pContextData = context_data
250
+ eddsa_params.ulContextDataLen = <CK_ULONG> len(context_data)
251
+
252
+ elif mechanism in (
253
+ Mechanism.ECDH1_DERIVE,
254
+ Mechanism.ECDH1_COFACTOR_DERIVE):
255
+ paramlen = sizeof(CK_ECDH1_DERIVE_PARAMS)
256
+ self.param = ecdh1_params = \
257
+ <CK_ECDH1_DERIVE_PARAMS *> PyMem_Malloc(paramlen)
258
+
259
+ (ecdh1_params.kdf, shared_data, public_data) = param
260
+
261
+ if shared_data is None:
262
+ ecdh1_params.pSharedData = NULL
263
+ ecdh1_params.ulSharedDataLen = 0
264
+ else:
265
+ ecdh1_params.pSharedData = shared_data
266
+ ecdh1_params.ulSharedDataLen = <CK_ULONG> len(shared_data)
267
+
268
+ ecdh1_params.pPublicData = public_data
269
+ ecdh1_params.ulPublicDataLen = <CK_ULONG> len(public_data)
270
+
271
+ elif mechanism == Mechanism.AES_CBC_ENCRYPT_DATA:
272
+ paramlen = sizeof(CK_AES_CBC_ENCRYPT_DATA_PARAMS)
273
+ self.param = aes_cbc_params = \
274
+ <CK_AES_CBC_ENCRYPT_DATA_PARAMS *> PyMem_Malloc(paramlen)
275
+ (iv, data) = param
276
+ aes_cbc_params.iv = iv[:16]
277
+ aes_cbc_params.pData = <CK_BYTE *> data
278
+ aes_cbc_params.length = <CK_ULONG> len(data)
279
+
280
+ elif mechanism == Mechanism.AES_GCM:
281
+ paramlen = sizeof(CK_GCM_PARAMS)
282
+ if not isinstance(param, GCMParams):
283
+ raise TypeError
284
+ self.param = gcm_params = <CK_GCM_PARAMS *> PyMem_Malloc(paramlen)
285
+ gcm_params.pIv = <CK_BYTE *> param.nonce
286
+ gcm_params.ulIvLen = <CK_ULONG> len(param.nonce)
287
+ gcm_params.ulIvBits = <CK_ULONG> len(param.nonce) * 8
288
+ if param.aad is not None:
289
+ gcm_params.pAAD = <CK_BYTE *> param.aad
290
+ gcm_params.ulAADLen = <CK_ULONG> len(param.aad)
291
+ else:
292
+ gcm_params.pAAD = NULL
293
+ gcm_params.ulAADLen = 0
294
+ gcm_params.ulTagBits = <CK_ULONG> param.tag_bits
295
+
296
+ elif mechanism == Mechanism.AES_CTR:
297
+ paramlen = sizeof(CK_AES_CTR_PARAMS)
298
+ self.param = aes_ctr_params = <CK_AES_CTR_PARAMS *> PyMem_Malloc(paramlen)
299
+ # use a wrapper type to not break the forwards compat rule for params specified as `bytes`
300
+ if not isinstance(param, CTRParams):
301
+ raise TypeError
302
+ aes_ctr_params.ulCounterBits = (16 - len(param.nonce)) * 8
303
+ aes_ctr_params.cb = param.nonce + b"\x00" * (15 - len(param.nonce)) + b"\x01"
304
+
305
+ elif param is None:
306
+ self.data.pParameter = NULL
307
+ paramlen = 0
308
+
309
+ else:
310
+ raise ArgumentsBad("Unexpected argument to mechanism_param")
311
+
312
+ self.data.mechanism = mechanism
313
+ self.data.ulParameterLen = <CK_ULONG> paramlen
314
+
315
+ if self.param != NULL:
316
+ self.data.pParameter = self.param
317
+
318
+ def __dealloc__(self):
319
+ PyMem_Free(self.data)
320
+ PyMem_Free(self.param)
321
+
322
+
323
+ cdef class Slot(HasFuncList, types.Slot):
324
+ """Extend Slot with implementation."""
325
+
326
+ cdef readonly CK_SLOT_ID slot_id
327
+ cdef readonly str slot_description
328
+ cdef readonly str manufacturer_id
329
+ cdef CK_FLAGS slot_flags
330
+ cdef CK_VERSION _hw_version
331
+ cdef CK_VERSION _fw_version
332
+ cdef CK_VERSION _cryptoki_version
333
+
334
+ @staticmethod
335
+ cdef Slot make(CK_FUNCTION_LIST *funclist, CK_SLOT_ID slot_id, CK_SLOT_INFO info, CK_VERSION cryptoki_version):
336
+ description = info.slotDescription[:sizeof(info.slotDescription)]
337
+ manufacturer_id = info.manufacturerID[:sizeof(info.manufacturerID)]
338
+
339
+ cdef Slot slot = Slot.__new__(Slot)
340
+ slot.funclist = funclist
341
+
342
+ slot.slot_id = slot_id
343
+ slot.slot_description = _CK_UTF8CHAR_to_str(description)
344
+ slot.manufacturer_id = _CK_UTF8CHAR_to_str(manufacturer_id)
345
+ slot._hw_version = info.hardwareVersion
346
+ slot._fw_version = info.firmwareVersion
347
+ slot._cryptoki_version = cryptoki_version
348
+ slot.slot_flags = info.flags
349
+ return slot
350
+
351
+ def __init__(self):
352
+ raise TypeError
353
+
354
+ @property
355
+ def flags(self):
356
+ """Capabilities of this slot (:class:`SlotFlag`)."""
357
+ return SlotFlag(self.slot_flags)
358
+
359
+ @property
360
+ def hardware_version(self):
361
+ """Hardware version (:class:`tuple`)."""
362
+ return _CK_VERSION_to_tuple(self._hw_version)
363
+
364
+ @property
365
+ def firmware_version(self):
366
+ """Firmware version (:class:`tuple`)."""
367
+ return _CK_VERSION_to_tuple(self._fw_version)
368
+
369
+ @property
370
+ def cryptoki_version(self):
371
+ """PKCS#11 (cryptoki) API version (:class:`tuple`)."""
372
+ return _CK_VERSION_to_tuple(self._cryptoki_version)
373
+
374
+ def get_token(self):
375
+ cdef CK_SLOT_ID slot_id = self.slot_id
376
+ cdef CK_TOKEN_INFO info
377
+ cdef CK_RV retval
378
+
379
+ with nogil:
380
+ retval = self.funclist.C_GetTokenInfo(slot_id, &info)
381
+ assertRV(retval)
382
+
383
+ return Token.make(self, info)
384
+
385
+ def get_mechanisms(self):
386
+ cdef CK_SLOT_ID slot_id = self.slot_id
387
+ cdef CK_ULONG count
388
+ cdef CK_RV retval
389
+
390
+ with nogil:
391
+ retval = self.funclist.C_GetMechanismList(slot_id, NULL, &count)
392
+ assertRV(retval)
393
+
394
+ if count == 0:
395
+ return set()
396
+
397
+ cdef CK_MECHANISM_TYPE [:] mechanisms = CK_ULONG_buffer(count)
398
+
399
+ with nogil:
400
+ retval = self.funclist.C_GetMechanismList(slot_id, &mechanisms[0], &count)
401
+ assertRV(retval)
402
+
403
+ return set(map(_CK_MECHANISM_TYPE_to_enum, mechanisms))
404
+
405
+ def get_mechanism_info(self, mechanism):
406
+ cdef CK_SLOT_ID slot_id = self.slot_id
407
+ cdef CK_MECHANISM_TYPE mech_type = mechanism
408
+ cdef CK_MECHANISM_INFO info
409
+ cdef CK_RV retval
410
+
411
+ with nogil:
412
+ retval = self.funclist.C_GetMechanismInfo(slot_id, mech_type, &info)
413
+ assertRV(retval)
414
+
415
+ return types.MechanismInfo(self, mechanism, **info)
416
+
417
+ def _identity(self):
418
+ return Slot.__name__, self.slot_id
419
+
420
+ def __str__(self):
421
+ return "\n".join(
422
+ (
423
+ "Slot Description: %s" % self.slot_description,
424
+ "Manufacturer ID: %s" % self.manufacturer_id,
425
+ "Hardware Version: %s.%s" % self.hardware_version,
426
+ "Firmware Version: %s.%s" % self.firmware_version,
427
+ "Flags: %s" % self.flags,
428
+ )
429
+ )
430
+
431
+ def __repr__(self):
432
+ return "<{klass} (slotID={slot_id} flags={flags})>".format(
433
+ klass=type(self).__name__, slot_id=self.slot_id, flags=str(self.flags)
434
+ )
435
+
436
+
437
+ cdef class Token(HasFuncList, types.Token):
438
+ """Extend Token with implementation."""
439
+
440
+ cdef readonly Slot slot
441
+ cdef readonly str label
442
+ cdef readonly bytes serial
443
+ cdef readonly str manufacturer_id
444
+ cdef readonly str model
445
+ cdef CK_FLAGS token_flags
446
+ cdef CK_VERSION _hw_version
447
+ cdef CK_VERSION _fw_version
448
+
449
+ @staticmethod
450
+ cdef Token make(Slot slot, CK_TOKEN_INFO info):
451
+ label = info.label[:sizeof(info.label)]
452
+ serial_number = info.serialNumber[:sizeof(info.serialNumber)]
453
+ model = info.model[:sizeof(info.model)]
454
+ manufacturer_id = info.manufacturerID[:sizeof(info.manufacturerID)]
455
+
456
+ cdef Token token = Token.__new__(Token)
457
+ token.funclist = slot.funclist
458
+ token.funclist30 = slot.funclist30
459
+ token.funclist32 = slot.funclist32
460
+ token.slot = slot
461
+ token.label = _CK_UTF8CHAR_to_str(label)
462
+ token.serial = serial_number.rstrip()
463
+ token.manufacturer_id = _CK_UTF8CHAR_to_str(manufacturer_id)
464
+ token.model = _CK_UTF8CHAR_to_str(model)
465
+ token._hw_version = info.hardwareVersion
466
+ token._fw_version = info.firmwareVersion
467
+ token.token_flags = info.flags
468
+ return token
469
+
470
+ def __init__(self):
471
+ raise TypeError
472
+
473
+ @property
474
+ def flags(self):
475
+ """Capabilities of this token (:class:`TokenFlag`)."""
476
+ return TokenFlag(self.token_flags)
477
+
478
+ @property
479
+ def hardware_version(self):
480
+ """Hardware version (:class:`tuple`)."""
481
+ return _CK_VERSION_to_tuple(self._hw_version)
482
+
483
+ @property
484
+ def firmware_version(self):
485
+ """Firmware version (:class:`tuple`)."""
486
+ return _CK_VERSION_to_tuple(self._fw_version)
487
+
488
+ def open(
489
+ self,
490
+ rw=False,
491
+ user_pin=None,
492
+ so_pin=None,
493
+ user_type=None,
494
+ attribute_mapper=None,
495
+ cancel_strategy=CancelStrategy.DEFAULT
496
+ ):
497
+ cdef CK_SLOT_ID slot_id = self.slot.slot_id
498
+ cdef CK_SESSION_HANDLE handle
499
+ cdef CK_FLAGS flags = CKF_SERIAL_SESSION
500
+ cdef CK_USER_TYPE final_user_type
501
+ cdef CK_UTF8CHAR *pin_data
502
+ cdef CK_ULONG pin_length
503
+ cdef CK_RV retval
504
+ cdef CK_USER_TYPE c_user_type
505
+
506
+ if rw:
507
+ flags |= CKF_RW_SESSION
508
+
509
+ if user_pin is not None and so_pin is not None:
510
+ raise ArgumentsBad("Set either `user_pin` or `so_pin`")
511
+ elif user_pin is PROTECTED_AUTH:
512
+ pin = None
513
+ c_user_type = user_type if user_type is not None else CKU_USER
514
+ elif so_pin is PROTECTED_AUTH:
515
+ pin = None
516
+ c_user_type = CKU_SO
517
+ elif user_pin is not None:
518
+ pin = user_pin.encode('utf-8')
519
+ c_user_type = user_type if user_type is not None else CKU_USER
520
+ elif so_pin is not None:
521
+ pin = so_pin.encode('utf-8')
522
+ c_user_type = CKU_SO
523
+ else:
524
+ pin = None
525
+ c_user_type = CKU_USER_NOBODY
526
+
527
+ with nogil:
528
+ retval = self.funclist.C_OpenSession(slot_id, flags, NULL, NULL, &handle)
529
+ assertRV(retval)
530
+
531
+ if so_pin is PROTECTED_AUTH or user_pin is PROTECTED_AUTH:
532
+ if self.flags & TokenFlag.PROTECTED_AUTHENTICATION_PATH:
533
+ with nogil:
534
+ retval = self.funclist.C_Login(handle, c_user_type, NULL, 0)
535
+ assertRV(retval)
536
+ else:
537
+ raise ArgumentsBad("Protected authentication is not supported by loaded module")
538
+ elif pin is not None:
539
+ pin_data = pin
540
+ pin_length = <CK_ULONG> len(pin)
541
+
542
+ with nogil:
543
+ retval = self.funclist.C_Login(handle, c_user_type, pin_data, pin_length)
544
+ assertRV(retval)
545
+
546
+ return Session.make(
547
+ self, handle,
548
+ rw=<bint> rw,
549
+ user_type=c_user_type,
550
+ mapper=attribute_mapper or AttributeMapper(),
551
+ cancel_strategy=<unsigned int> cancel_strategy
552
+ )
553
+
554
+ def __str__(self):
555
+ return self.label
556
+
557
+ def _identity(self):
558
+ return Token.__name__, self.slot
559
+
560
+ def __repr__(self):
561
+ return "<{klass} (label='{label}' serial={serial} flags={flags})>".format(
562
+ klass=type(self).__name__, label=self.label, serial=self.serial, flags=str(self.flags)
563
+ )
564
+
565
+
566
+ cdef class OperationContext:
567
+ cdef Session session
568
+ cdef bint active
569
+
570
+ def __cinit__(self, session, *args, **kwargs):
571
+ self.session = session
572
+ self.active = False
573
+
574
+ def __init__(self, session):
575
+ pass
576
+
577
+ def __enter__(self):
578
+ self.session.operation_lock.acquire()
579
+ self.active = True
580
+ self._initiate()
581
+ return self
582
+
583
+ def __exit__(self, exc_type, exc_val, exc_tb):
584
+ self._finalize(silent=False)
585
+
586
+ cdef _handle_final_retval(self, CK_RV retval) with gil:
587
+ self.active = False
588
+ self.session.operation_lock.release()
589
+ assertRV(retval)
590
+
591
+ cdef _operation_aware_assert(self, CK_RV retval) with gil:
592
+ if retval != CKR_BUFFER_TOO_SMALL and retval != CKR_OK:
593
+ # This is an error that terminated the operation
594
+ # We flag the operation as completed on our end as well.
595
+ # This is useful to track because there's no way to cleanly cancel
596
+ # cryptographic operations in PCKS#11 2.x.
597
+ self._handle_final_retval(retval)
598
+
599
+ def _initiate(self):
600
+ raise NotImplementedError
601
+
602
+ def _finalize(self, silent=False):
603
+ if self.active:
604
+ self.active = False
605
+ self.session.operation_lock.release()
606
+
607
+ def __del__(self):
608
+ self._finalize()
609
+
610
+
611
+ cdef class OperationWithBinaryOutput(OperationContext):
612
+
613
+ cdef MechanismWithParam mech
614
+
615
+ cdef CK_ULONG buffer_size
616
+ cdef CK_ULONG buffer_data_length
617
+ cdef CK_BYTE [:] output_buf
618
+
619
+ @staticmethod
620
+ cdef OperationWithBinaryOutput _setup(
621
+ type cls,
622
+ Session session,
623
+ MechanismWithParam mech,
624
+ CK_ULONG buffer_size
625
+ ) with gil:
626
+ cdef OperationWithBinaryOutput op = cls.__new__(cls, session)
627
+ op.mech = mech
628
+ if buffer_size > 0:
629
+ op.output_buf = CK_BYTE_buffer(buffer_size)
630
+ op.buffer_size = buffer_size
631
+ return op
632
+
633
+ cdef resize_buffer(self, CK_ULONG length):
634
+ self.output_buf = CK_BYTE_buffer(length)
635
+ self.buffer_size = length
636
+
637
+ cdef inline bytes current_output(self):
638
+ return bytes(self.output_buf[:self.buffer_data_length])
639
+
640
+ cdef CK_RV update_resizing_output(
641
+ self,
642
+ OperationUpdateWithResult op_update,
643
+ CK_BYTE *data,
644
+ CK_ULONG data_len,
645
+ ) with gil:
646
+
647
+ cdef CK_ULONG length = self.buffer_size
648
+ cdef CK_BYTE *output_buf_loc = &self.output_buf[0]
649
+ cdef CK_RV retval
650
+
651
+ with nogil:
652
+ retval = op_update(
653
+ self.session.handle, data, data_len, NULL, &length
654
+ )
655
+
656
+ if retval != CKR_OK:
657
+ return retval
658
+
659
+ if length > self.buffer_size:
660
+ self.resize_buffer(length)
661
+ output_buf_loc = &self.output_buf[0]
662
+
663
+ with nogil:
664
+ retval = op_update(
665
+ self.session.handle, data, data_len, output_buf_loc, &length
666
+ )
667
+ self.buffer_data_length = length
668
+ return retval
669
+
670
+ cdef CK_RV execute_resizing_output(self, OperationWithResult op) with gil:
671
+
672
+ cdef CK_ULONG length = self.buffer_size
673
+ cdef CK_BYTE *output_buf_loc = &self.output_buf[0]
674
+ cdef CK_RV retval
675
+
676
+ with nogil:
677
+ retval = op(self.session.handle, NULL, &length)
678
+
679
+ if retval != CKR_OK:
680
+ return retval
681
+
682
+ if length > self.buffer_size:
683
+ self.resize_buffer(length)
684
+ output_buf_loc = &self.output_buf[0]
685
+
686
+
687
+ with nogil:
688
+ retval = op(self.session.handle, output_buf_loc, &length)
689
+ self.buffer_data_length = length
690
+ return retval
691
+
692
+ cdef bytes process_fully(
693
+ self,
694
+ OperationUpdateWithResult op,
695
+ CK_BYTE *data,
696
+ CK_ULONG data_len
697
+ ) with gil:
698
+ cdef CK_RV retval = self.update_resizing_output(op, data, data_len)
699
+ self._handle_final_retval(retval)
700
+ return self.current_output()
701
+
702
+ cdef bytes update_with_result(
703
+ self, OperationUpdateWithResult op, CK_BYTE *data, CK_ULONG data_len
704
+ ) with gil:
705
+ cdef CK_RV retval = self.update_resizing_output(op, data, data_len)
706
+ self._operation_aware_assert(retval)
707
+ return self.current_output()
708
+
709
+ cdef update_no_output(
710
+ self, OperationUpdate op, CK_BYTE *data, CK_ULONG data_len
711
+ ) with gil:
712
+ cdef CK_RV retval
713
+ with nogil:
714
+ retval = op(self.session.handle, data, data_len)
715
+ self._operation_aware_assert(retval)
716
+
717
+ cdef bytes finish_with_output(self, OperationWithResult op_final) with gil:
718
+ cdef CK_RV retval = self.execute_resizing_output(op_final)
719
+ self._handle_final_retval(retval)
720
+ return self.current_output()
721
+
722
+
723
+ cdef class SearchIter(OperationContext):
724
+ """Iterate a search for objects on a session."""
725
+
726
+ cdef AttributeList template
727
+ cdef CK_ULONG batch_size
728
+
729
+ def __init__(self, session, attrs, batch_size):
730
+ cdef Session _session = session
731
+ cdef AttributeList template = _session.make_attribute_list(attrs)
732
+ self.template = template
733
+ self.batch_size = batch_size
734
+ super().__init__(_session)
735
+
736
+ def __iter__(self):
737
+ return self
738
+
739
+ def _batch_iter(self, results, count):
740
+ for ix in range(count):
741
+ yield make_object(self.session, results[ix])
742
+
743
+ def __next__(self):
744
+ """Get the next batch of objects."""
745
+ cdef CK_SESSION_HANDLE handle = self.session.handle
746
+ cdef CK_OBJECT_HANDLE obj
747
+ cdef CK_ULONG count
748
+ cdef CK_RV retval
749
+
750
+ cdef CK_OBJECT_HANDLE [:] results = CK_ULONG_buffer(self.batch_size)
751
+
752
+ with nogil:
753
+ retval = self.session.funclist.C_FindObjects(handle, &results[0], self.batch_size, &count)
754
+ assertRV(retval)
755
+
756
+ if count == 0:
757
+ self._finalize()
758
+ raise StopIteration()
759
+ else:
760
+ return self._batch_iter(results, count)
761
+
762
+ def _initiate(self):
763
+ cdef CK_SESSION_HANDLE handle = self.session.handle
764
+ cdef CK_ATTRIBUTE *attr_data = self.template.data
765
+ cdef CK_ULONG attr_count = self.template.count
766
+ cdef CK_RV retval
767
+
768
+ with nogil:
769
+ retval = self.session.funclist.C_FindObjectsInit(handle, attr_data, attr_count)
770
+ assertRV(retval)
771
+
772
+ def _finalize(self, silent=False):
773
+ """Finish the operation."""
774
+ cdef CK_SESSION_HANDLE handle = self.session.handle
775
+ cdef CK_RV retval
776
+
777
+ if self.active:
778
+ with nogil:
779
+ retval = self.session.funclist.C_FindObjectsFinal(handle)
780
+ if not silent:
781
+ self._handle_final_retval(retval)
782
+
783
+
784
+ cdef class DigestOperation(OperationWithBinaryOutput):
785
+
786
+ @staticmethod
787
+ cdef DigestOperation setup(
788
+ Session session,
789
+ MechanismWithParam mech,
790
+ CK_ULONG buffer_size
791
+ ) with gil:
792
+ cdef DigestOperation op = <DigestOperation> OperationWithBinaryOutput._setup(
793
+ DigestOperation, session, mech, buffer_size
794
+ )
795
+ return op
796
+
797
+ def _initiate(self):
798
+ cdef CK_RV retval
799
+ with nogil:
800
+ retval = self.session.funclist.C_DigestInit(self.session.handle, self.mech.data)
801
+ self._operation_aware_assert(retval)
802
+
803
+ cdef bytes digest_process_fully(self, CK_BYTE *data, CK_ULONG data_len) with gil:
804
+ cdef Session session = self.session
805
+ return self.process_fully(session.funclist.C_Digest, data, data_len)
806
+
807
+ cdef void update_digest(self, CK_BYTE *data, CK_ULONG data_len):
808
+ self.update_no_output(self.session.funclist.C_DigestUpdate, data, data_len)
809
+
810
+ cdef void update_digest_with_key(self, CK_OBJECT_HANDLE key):
811
+ cdef Session session = self.session
812
+ with nogil:
813
+ retval = session.funclist.C_DigestKey(session.handle, key)
814
+ self._operation_aware_assert(retval)
815
+
816
+ def ingest_chunks(self, chunks):
817
+ cdef CK_BYTE *data_ptr
818
+ cdef CK_ULONG data_len
819
+ cdef CK_OBJECT_HANDLE key
820
+
821
+ for chunk in chunks:
822
+ if not chunk:
823
+ continue
824
+ if isinstance(chunk, types.Key):
825
+ key = chunk.handle
826
+ self.update_digest_with_key(key)
827
+ else:
828
+ data_ptr = chunk
829
+ data_len = <CK_ULONG> len(chunk)
830
+ self.update_digest(data_ptr, data_len)
831
+
832
+ cdef bytes finish(self):
833
+ return self.finish_with_output(self.session.funclist.C_DigestFinal)
834
+
835
+ def _finalize(self, silent=False):
836
+ cdef Session session = self.session
837
+ if self.active:
838
+ self.execute_resizing_output(session.funclist.C_DigestFinal)
839
+ super()._finalize(silent=silent)
840
+
841
+
842
+ def merge_templates(default_template, *user_templates):
843
+ template = default_template.copy()
844
+
845
+ for user_template in user_templates:
846
+ if user_template is not None:
847
+ template.update(user_template)
848
+
849
+ return {
850
+ key: value
851
+ for key, value in template.items()
852
+ if value is not DEFAULT
853
+ }
854
+
855
+
856
+ cdef _check_key_type(key_type):
857
+ if not isinstance(key_type, KeyType):
858
+ raise ArgumentsBad("`key_type` must be KeyType.")
859
+
860
+
861
+ cdef _check_bit_length(value, name, required=False):
862
+ if value is None and not required:
863
+ return
864
+ if not isinstance(value, int):
865
+ raise ArgumentsBad("`%s` is the length in bits." % name)
866
+
867
+
868
+ cdef _resolve_capabilities(key_type, capabilities):
869
+ if capabilities is not None:
870
+ return capabilities
871
+ try:
872
+ return DEFAULT_KEY_CAPABILITIES[key_type]
873
+ except KeyError:
874
+ raise ArgumentsBad("No default capabilities for this key "
875
+ "type. Please specify `capabilities`.")
876
+
877
+
878
+ cdef class Session(HasFuncList, types.Session):
879
+ """Extend Session with implementation."""
880
+
881
+ cdef readonly CK_SESSION_HANDLE handle
882
+ cdef readonly Token token
883
+ cdef readonly bint rw
884
+ cdef CK_USER_TYPE _user_type
885
+ cdef object operation_lock
886
+ cdef object attribute_mapper
887
+ cdef unsigned int cancel_strategy
888
+
889
+ @staticmethod
890
+ cdef Session make(
891
+ Token token,
892
+ CK_SESSION_HANDLE handle,
893
+ bint rw,
894
+ CK_USER_TYPE user_type,
895
+ object mapper,
896
+ unsigned int cancel_strategy
897
+ ):
898
+ cdef Session session = Session.__new__(Session)
899
+
900
+ session.funclist = token.funclist
901
+ session.funclist30 = token.funclist30
902
+ session.funclist32 = token.funclist32
903
+ session.token = token
904
+
905
+ session.handle = handle
906
+ # Big operation lock prevents other threads from entering/reentering
907
+ # operations. If the same thread enters the lock, they will get a
908
+ # Cryptoki warning
909
+ session.operation_lock = RLock()
910
+
911
+ session.rw = rw
912
+ session._user_type = user_type
913
+ session.attribute_mapper = mapper
914
+ session.cancel_strategy = cancel_strategy
915
+ return session
916
+
917
+ def __init__(self):
918
+ raise TypeError
919
+
920
+ def _identity(self):
921
+ return Session.__name__, self.token, self.handle
922
+
923
+ @property
924
+ def user_type(self):
925
+ """User type for this session (:class:`pkcs11.constants.UserType` or :class:`int` for vendor-specific values)."""
926
+ try:
927
+ return UserType(self._user_type)
928
+ except ValueError:
929
+ # Return raw value for vendor-specific user types
930
+ return self._user_type
931
+
932
+ def close(self):
933
+ cdef CK_SESSION_HANDLE handle = self.handle
934
+ cdef CK_RV retval
935
+
936
+ if self._user_type != CKU_USER_NOBODY:
937
+ with nogil:
938
+ retval = self.funclist.C_Logout(handle)
939
+ assertRV(retval)
940
+
941
+ with nogil:
942
+ retval = self.funclist.C_CloseSession(handle)
943
+ assertRV(retval)
944
+
945
+ def get_objects(self, attrs=None, batch_size=10):
946
+ with SearchIter(self, attrs or {}, batch_size) as op:
947
+ for batch in op:
948
+ yield from batch
949
+
950
+ def reaffirm_credentials(self, pin):
951
+ cdef CK_UTF8CHAR *pin_data
952
+ cdef CK_ULONG pin_length
953
+
954
+ pin = pin.encode('utf-8')
955
+ pin_data = pin
956
+ pin_length = <CK_ULONG> len(pin)
957
+ user_type = CKU_CONTEXT_SPECIFIC
958
+
959
+ with nogil:
960
+ retval = self.funclist.C_Login(self.handle, user_type, pin_data, pin_length)
961
+ assertRV(retval)
962
+
963
+ def create_object(self, attrs):
964
+ template = self.make_attribute_list(attrs)
965
+
966
+ cdef CK_OBJECT_HANDLE handle = self.handle
967
+ cdef CK_ATTRIBUTE *attr_data = template.data
968
+ cdef CK_ULONG attr_count = template.count
969
+ cdef CK_OBJECT_HANDLE new
970
+ cdef CK_RV retval
971
+
972
+ with nogil:
973
+ retval = self.funclist.C_CreateObject(handle, attr_data, attr_count, &new)
974
+ assertRV(retval)
975
+
976
+ return make_object(self, new)
977
+
978
+ def create_domain_parameters(self, key_type, attrs,
979
+ local=False, store=False):
980
+ if local and store:
981
+ raise ArgumentsBad("Cannot set both `local` and `store`")
982
+
983
+ attrs = dict(attrs)
984
+ attrs[Attribute.CLASS] = ObjectClass.DOMAIN_PARAMETERS
985
+ attrs[Attribute.KEY_TYPE] = key_type
986
+ attrs[Attribute.TOKEN] = store
987
+
988
+ if local:
989
+ return LocalDomainParameters(self, attrs)
990
+ else:
991
+ return self.create_object(attrs)
992
+
993
+ def generate_domain_parameters(self, key_type, param_length, store=False,
994
+ mechanism=None, mechanism_param=None,
995
+ template=None):
996
+ _check_key_type(key_type)
997
+ _check_bit_length(param_length, "param_length", required=True)
998
+
999
+ mech = MechanismWithParam(
1000
+ key_type, DEFAULT_PARAM_GENERATE_MECHANISMS,
1001
+ mechanism, mechanism_param)
1002
+
1003
+ template_ = {
1004
+ Attribute.CLASS: ObjectClass.DOMAIN_PARAMETERS,
1005
+ Attribute.TOKEN: store,
1006
+ Attribute.PRIME_BITS: param_length,
1007
+ }
1008
+ attrs = self.make_attribute_list(merge_templates(template_, template))
1009
+
1010
+ return self.generate_key_from_attrs(attrs, mech)
1011
+
1012
+ cdef AttributeList make_attribute_list(self, template):
1013
+ return AttributeList.from_template(dict(template), self.attribute_mapper)
1014
+
1015
+ def generate_key(self, key_type, key_length=None,
1016
+ id=None, label=None,
1017
+ store=False, capabilities=None,
1018
+ mechanism=None, mechanism_param=None,
1019
+ template=None):
1020
+
1021
+ _check_key_type(key_type)
1022
+ _check_bit_length(key_length, "key_length")
1023
+ capabilities = _resolve_capabilities(key_type, capabilities)
1024
+
1025
+ mech = MechanismWithParam(
1026
+ key_type, DEFAULT_GENERATE_MECHANISMS,
1027
+ mechanism, mechanism_param)
1028
+
1029
+ template_ = self.attribute_mapper.secret_key_template(
1030
+ capabilities=capabilities, id_=id, label=label, store=store,
1031
+ )
1032
+ # Build attributes
1033
+ if key_type not in (KeyType.DES2, KeyType.DES3, KeyType.GOST28147, KeyType.SEED):
1034
+ if key_length is None:
1035
+ raise ArgumentsBad("Must provide `key_length'")
1036
+
1037
+ template_[Attribute.VALUE_LEN] = key_length // 8 # In bytes
1038
+
1039
+ attrs = self.make_attribute_list(merge_templates(template_, template))
1040
+
1041
+ return self.generate_key_from_attrs(attrs, mech)
1042
+
1043
+ cdef object generate_key_from_attrs(
1044
+ self, AttributeList attrs, MechanismWithParam mech
1045
+ ):
1046
+ cdef CK_SESSION_HANDLE handle = self.handle
1047
+ cdef CK_MECHANISM *mech_data = mech.data
1048
+ cdef CK_ATTRIBUTE *attr_data = attrs.data
1049
+ cdef CK_ULONG attr_count = attrs.count
1050
+ cdef CK_OBJECT_HANDLE key
1051
+
1052
+ with nogil:
1053
+ retval = self.funclist.C_GenerateKey(handle, mech_data, attr_data, attr_count, &key)
1054
+ assertRV(retval)
1055
+
1056
+ return make_object(self, key)
1057
+
1058
+
1059
+ def _generate_keypair(self, key_type, key_length=None,
1060
+ id=None, label=None,
1061
+ store=False, capabilities=None,
1062
+ mechanism=None, mechanism_param=None,
1063
+ public_template=None, private_template=None):
1064
+
1065
+ _check_key_type(key_type)
1066
+ _check_bit_length(key_length, "key_length")
1067
+ capabilities = _resolve_capabilities(key_type, capabilities)
1068
+
1069
+ mech = MechanismWithParam(
1070
+ key_type, DEFAULT_GENERATE_MECHANISMS,
1071
+ mechanism, mechanism_param)
1072
+
1073
+ public_template_ = self.attribute_mapper.public_key_template(
1074
+ id_=id, label=label, store=store, capabilities=capabilities,
1075
+ )
1076
+ private_template_ = self.attribute_mapper.private_key_template(
1077
+ id_=id, label=label, store=store, capabilities=capabilities,
1078
+ )
1079
+
1080
+ if key_type is KeyType.RSA:
1081
+ if key_length is None:
1082
+ raise ArgumentsBad("Must provide `key_length'")
1083
+
1084
+ # Some PKCS#11 implementations don't default this, it makes sense
1085
+ # to do it here
1086
+ public_template_.update({
1087
+ Attribute.PUBLIC_EXPONENT: b'\1\0\1',
1088
+ Attribute.MODULUS_BITS: key_length,
1089
+ })
1090
+
1091
+ elif key_type is KeyType.ML_DSA:
1092
+ if public_template is None or Attribute.PARAMETER_SET not in public_template:
1093
+ raise ArgumentsBad(
1094
+ "ML-DSA key generation requires `Attribute.PARAMETER_SET` "
1095
+ "in `public_template` (e.g. MLDSAParameterSet.ML_DSA_65)."
1096
+ )
1097
+
1098
+ elif key_type is KeyType.ML_KEM:
1099
+ if public_template is None or Attribute.PARAMETER_SET not in public_template:
1100
+ raise ArgumentsBad(
1101
+ "ML-KEM key generation requires `Attribute.PARAMETER_SET` "
1102
+ "in `public_template` (e.g. MLKEMParameterSet.ML_KEM_768)."
1103
+ )
1104
+ public_template_[Attribute.ENCAPSULATE] = True
1105
+ private_template_[Attribute.DECAPSULATE] = True
1106
+
1107
+ public_attrs = self.make_attribute_list(merge_templates(public_template_, public_template))
1108
+ private_attrs = self.make_attribute_list(merge_templates(private_template_, private_template))
1109
+ return self.generate_keypair_from_attrs(public_attrs, private_attrs, mech)
1110
+
1111
+ cdef tuple generate_keypair_from_attrs(
1112
+ self,
1113
+ AttributeList public_attrs,
1114
+ AttributeList private_attrs,
1115
+ MechanismWithParam mech
1116
+ ):
1117
+
1118
+ cdef CK_SESSION_HANDLE handle = self.handle
1119
+ cdef CK_MECHANISM *mech_data = mech.data
1120
+ cdef CK_ATTRIBUTE *public_attr_data = public_attrs.data
1121
+ cdef CK_ULONG public_attr_count = public_attrs.count
1122
+ cdef CK_ATTRIBUTE *private_attr_data = private_attrs.data
1123
+ cdef CK_ULONG private_attr_count = private_attrs.count
1124
+ cdef CK_OBJECT_HANDLE public_key
1125
+ cdef CK_OBJECT_HANDLE private_key
1126
+ cdef CK_RV retval
1127
+
1128
+ with nogil:
1129
+ retval = self.funclist.C_GenerateKeyPair(handle, mech_data, public_attr_data, public_attr_count, private_attr_data, private_attr_count, &public_key, &private_key)
1130
+ assertRV(retval)
1131
+
1132
+ return (make_object(self, public_key),
1133
+ make_object(self, private_key))
1134
+
1135
+ def seed_random(self, seed):
1136
+ cdef CK_SESSION_HANDLE handle = self.handle
1137
+ cdef CK_BYTE *seed_data = seed
1138
+ cdef CK_ULONG seed_len = <CK_ULONG> len(seed)
1139
+ cdef CK_RV retval
1140
+
1141
+ with nogil:
1142
+ retval = self.funclist.C_SeedRandom(handle, seed_data, seed_len)
1143
+ assertRV(retval)
1144
+
1145
+ def generate_random(self, nbits):
1146
+ cdef CK_SESSION_HANDLE handle = self.handle
1147
+ cdef CK_ULONG length = nbits // 8
1148
+ cdef CK_CHAR [:] random = CK_BYTE_buffer(length)
1149
+ cdef CK_RV retval
1150
+
1151
+ with nogil:
1152
+ retval = self.funclist.C_GenerateRandom(handle, &random[0], length)
1153
+ assertRV(retval)
1154
+
1155
+ return bytes(random)
1156
+
1157
+ def __digest_operation(self, mechanism, mechanism_param):
1158
+ mech = MechanismWithParam(
1159
+ None, {},
1160
+ mechanism, mechanism_param)
1161
+ return DigestOperation.setup(self, mech, 1024)
1162
+
1163
+ def _digest(self, data, mechanism=None, mechanism_param=None):
1164
+ cdef CK_BYTE *data_ptr = data
1165
+ cdef CK_ULONG data_len = <CK_ULONG> len(data)
1166
+ cdef CK_RV retval
1167
+
1168
+ cdef DigestOperation op = self.__digest_operation(mechanism, mechanism_param)
1169
+ with op:
1170
+ return op.digest_process_fully(data_ptr, data_len)
1171
+
1172
+ def _digest_generator(self, data, mechanism=None, mechanism_param=None):
1173
+
1174
+ cdef DigestOperation op = self.__digest_operation(mechanism, mechanism_param)
1175
+ with op:
1176
+ op.ingest_chunks(data)
1177
+ return op.finish()
1178
+
1179
+ def set_pin(self, old_pin, new_pin):
1180
+ cdef CK_ULONG old_pin_length
1181
+ cdef CK_ULONG new_pin_length
1182
+ cdef CK_OBJECT_HANDLE handle = self.handle
1183
+ cdef CK_UTF8CHAR *old_pin_data
1184
+ cdef CK_UTF8CHAR *new_pin_data
1185
+ cdef CK_RV retval
1186
+
1187
+ pin_old = old_pin.encode('utf-8')
1188
+ pin_new = new_pin.encode('utf-8')
1189
+
1190
+ old_pin_data = pin_old
1191
+ new_pin_data = pin_new
1192
+ old_pin_length = len(pin_old)
1193
+ new_pin_length = len(pin_new)
1194
+
1195
+ with nogil:
1196
+ retval = self.funclist.C_SetPIN(handle, old_pin_data, old_pin_length, new_pin_data, new_pin_length)
1197
+ assertRV(retval)
1198
+
1199
+ def init_pin(self, pin):
1200
+ cdef CK_OBJECT_HANDLE handle = self.handle
1201
+ cdef CK_UTF8CHAR *pin_data
1202
+ cdef CK_ULONG pin_length
1203
+ cdef CK_RV retval
1204
+
1205
+ pin = pin.encode('utf-8')
1206
+
1207
+ pin_data = pin
1208
+ pin_length = len(pin)
1209
+
1210
+ with nogil:
1211
+ retval = self.funclist.C_InitPIN(handle, pin_data, pin_length)
1212
+ assertRV(retval)
1213
+
1214
+ cdef class ObjectHandleWrapper(HasFuncList):
1215
+ """
1216
+ Class implementing generic operations on PKCS#11 objects.
1217
+ """
1218
+
1219
+ cdef readonly Session session
1220
+ cdef readonly CK_OBJECT_HANDLE handle
1221
+
1222
+ @staticmethod
1223
+ cdef ObjectHandleWrapper wrap(Session session, CK_OBJECT_HANDLE handle):
1224
+ cdef ObjectHandleWrapper obj = ObjectHandleWrapper.__new__(ObjectHandleWrapper)
1225
+ obj.funclist = session.funclist
1226
+ obj.session = session
1227
+ obj.handle = handle
1228
+ return obj
1229
+
1230
+ def __init__(self):
1231
+ raise TypeError
1232
+
1233
+ cdef AttributeList get_attribute_list(self, CK_ATTRIBUTE_TYPE * keys, CK_ULONG total) with gil:
1234
+ cdef CK_SESSION_HANDLE handle = self.session.handle
1235
+ cdef CK_OBJECT_HANDLE obj = self.handle
1236
+ cdef CK_ULONG ix = 0
1237
+ cdef CK_ULONG retrievable = 0
1238
+ cdef CK_ATTRIBUTE *tpl = <CK_ATTRIBUTE *> PyMem_Malloc(total * sizeof(CK_ATTRIBUTE))
1239
+ cdef CK_RV retval
1240
+
1241
+ for ix in range(total):
1242
+ tpl[ix].type = keys[ix]
1243
+ tpl[ix].pValue = NULL
1244
+ tpl[ix].ulValueLen = <CK_ULONG> 0
1245
+
1246
+ with nogil:
1247
+ retval = self.funclist.C_GetAttributeValue(handle, obj, tpl, total)
1248
+
1249
+ for ix in range(total):
1250
+ if tpl[ix].ulValueLen != CK_UNAVAILABLE_INFORMATION:
1251
+ # overwrite the template at position 'retrievable' in the buffer
1252
+ tpl[retrievable].type = tpl[ix].type
1253
+ tpl[retrievable].pValue = PyMem_Malloc(tpl[ix].ulValueLen)
1254
+ if tpl[retrievable].pValue is NULL:
1255
+ raise MemoryError()
1256
+ tpl[retrievable].ulValueLen = tpl[ix].ulValueLen
1257
+ retrievable += 1
1258
+ # The spec prohibits returning CK_UNAVAILABLE_INFORMATION
1259
+ # together with CKR_OK, but some tokens do that anyway.
1260
+ # Let's be defensive and map that to a proper error,
1261
+ if tpl[ix].ulValueLen == CK_UNAVAILABLE_INFORMATION and retval == CKR_OK:
1262
+ retval = CKR_FUNCTION_FAILED
1263
+ break
1264
+
1265
+ # when this gets GC'd, the __dealloc__ will clean up our buffers
1266
+ result = AttributeList.from_owned_pointer(tpl, retrievable)
1267
+ if retrievable:
1268
+ with nogil:
1269
+ retval = self.funclist.C_GetAttributeValue(handle, obj, tpl, retrievable)
1270
+ for ix in range(retrievable):
1271
+ # Ensure the data returned from the token is sane
1272
+ if tpl[ix].ulValueLen == CK_UNAVAILABLE_INFORMATION or tpl[ix].pValue is NULL:
1273
+ retval = CKR_FUNCTION_FAILED
1274
+ break
1275
+ assertRV(retval)
1276
+ return result
1277
+
1278
+
1279
+ def __getitem__(self, key):
1280
+ cdef CK_ATTRIBUTE_TYPE key_t = key
1281
+ return self.get_attribute_list(&key_t, 1).at_index(0, self.session.attribute_mapper)
1282
+
1283
+ def __setitem__(self, key, value):
1284
+ cdef CK_SESSION_HANDLE handle = self.session.handle
1285
+ cdef CK_OBJECT_HANDLE obj = self.handle
1286
+ cdef CK_ATTRIBUTE template
1287
+ cdef CK_RV retval
1288
+
1289
+ value = self.session.attribute_mapper.pack_attribute(key, value)
1290
+
1291
+ template.type = key
1292
+ template.pValue = <CK_CHAR *> value
1293
+ template.ulValueLen = <CK_ULONG>len(value)
1294
+
1295
+ with nogil:
1296
+ retval = self.funclist.C_SetAttributeValue(handle, obj, &template, 1)
1297
+ assertRV(retval)
1298
+
1299
+ def destroy(self):
1300
+ cdef CK_SESSION_HANDLE handle = self.session.handle
1301
+ cdef CK_OBJECT_HANDLE obj = self.handle
1302
+ cdef CK_RV retval
1303
+
1304
+ with nogil:
1305
+ retval = self.session.funclist.C_DestroyObject(handle, obj)
1306
+ assertRV(retval)
1307
+
1308
+ def copy(self, attrs):
1309
+ template = self.session.make_attribute_list(attrs)
1310
+
1311
+ cdef CK_SESSION_HANDLE handle = self.session.handle
1312
+ cdef CK_OBJECT_HANDLE obj = self.handle
1313
+ cdef CK_ATTRIBUTE *attr_data = template.data
1314
+ cdef CK_ULONG attr_count = template.count
1315
+ cdef CK_OBJECT_HANDLE new_obj
1316
+ cdef CK_RV retval
1317
+
1318
+ with nogil:
1319
+ retval = self.session.funclist.C_CopyObject(handle, obj, attr_data, attr_count, &new_obj)
1320
+ assertRV(retval)
1321
+ return new_obj
1322
+
1323
+ def identity(self):
1324
+ return ObjectHandleWrapper.__name__, self.session, self.handle
1325
+
1326
+
1327
+ class Object(types.Object):
1328
+ """Expand Object with an implementation."""
1329
+
1330
+ def __init__(self, wrapper: ObjectHandleWrapper):
1331
+ self.wrapper = wrapper
1332
+
1333
+ def __getitem__(self, item):
1334
+ return self.wrapper[item]
1335
+
1336
+ def __setitem__(self, key, value):
1337
+ self.wrapper[key] = value
1338
+
1339
+ def get_attributes(self, keys):
1340
+ cdef ObjectHandleWrapper wrapper = self.wrapper
1341
+ cdef CK_ULONG total = len(keys)
1342
+
1343
+ if not total:
1344
+ return {}
1345
+
1346
+ cdef CK_ATTRIBUTE_TYPE * key_ptr = <CK_ATTRIBUTE_TYPE *> PyMem_Malloc(total * sizeof(CK_ATTRIBUTE_TYPE))
1347
+ cdef CK_ULONG ix = 0
1348
+ for ix, key in enumerate(keys):
1349
+ key_ptr[ix] = <CK_ATTRIBUTE_TYPE> key
1350
+
1351
+ try:
1352
+ result = wrapper.get_attribute_list(key_ptr, total).as_dict(wrapper.session.attribute_mapper)
1353
+ finally:
1354
+ PyMem_Free(key_ptr)
1355
+ return result
1356
+
1357
+ @property
1358
+ def session(self):
1359
+ return self.wrapper.session
1360
+
1361
+ @property
1362
+ def handle(self):
1363
+ return self.wrapper.handle
1364
+
1365
+ def copy(self, attrs):
1366
+ new_obj = self.wrapper.copy(attrs)
1367
+ return make_object(self.wrapper.session, new_obj)
1368
+
1369
+ def destroy(self):
1370
+ self.wrapper.destroy()
1371
+
1372
+ def _identity(self):
1373
+ return Object.__name__, self.wrapper.identity()
1374
+
1375
+
1376
+ cdef object make_object(Session session, CK_OBJECT_HANDLE handle) with gil:
1377
+ """
1378
+ Make an object with the right bases for its class and capabilities.
1379
+ """
1380
+ wrapper = ObjectHandleWrapper.wrap(session, handle)
1381
+
1382
+ cdef CK_ATTRIBUTE_TYPE[10] attr_keys = [
1383
+ Attribute.CLASS,
1384
+ Attribute.ENCRYPT,
1385
+ Attribute.DECRYPT,
1386
+ Attribute.SIGN,
1387
+ Attribute.VERIFY,
1388
+ Attribute.WRAP,
1389
+ Attribute.UNWRAP,
1390
+ Attribute.DERIVE,
1391
+ Attribute.ENCAPSULATE,
1392
+ Attribute.DECAPSULATE,
1393
+ ]
1394
+
1395
+ try:
1396
+ # Determine a list of base classes to manufacture our class with
1397
+ try:
1398
+ attributes = wrapper.get_attribute_list(&attr_keys[0], 10)
1399
+ except PKCS11Error:
1400
+ # retry fetching the flags one by one, some tokens do not implement error handling
1401
+ # on bulk fetches correctly.
1402
+ attributes = {}
1403
+ for key in attr_keys:
1404
+ try:
1405
+ attributes[key] = wrapper[key]
1406
+ except (AttributeTypeInvalid, AttributeSensitive, FunctionFailed):
1407
+ continue
1408
+
1409
+ object_class = attributes.get(Attribute.CLASS, session.attribute_mapper)
1410
+ bases = (_CLASS_MAP[object_class],)
1411
+
1412
+ # Build a list of mixins for this new class
1413
+ for attribute, mixin in (
1414
+ (Attribute.ENCRYPT, EncryptMixin),
1415
+ (Attribute.DECRYPT, DecryptMixin),
1416
+ (Attribute.SIGN, SignMixin),
1417
+ (Attribute.VERIFY, VerifyMixin),
1418
+ (Attribute.WRAP, WrapMixin),
1419
+ (Attribute.UNWRAP, UnwrapMixin),
1420
+ (Attribute.DERIVE, DeriveMixin),
1421
+ (Attribute.ENCAPSULATE, EncapsulateMixin),
1422
+ (Attribute.DECAPSULATE, DecapsulateMixin),
1423
+ ):
1424
+ try:
1425
+ if attributes.get(attribute, session.attribute_mapper):
1426
+ bases += (mixin,)
1427
+ except KeyError:
1428
+ pass
1429
+
1430
+ bases += (Object,)
1431
+
1432
+ # Manufacture a class with the right capabilities.
1433
+ klass = type(bases[0].__name__, bases, {})
1434
+
1435
+ return klass(wrapper)
1436
+
1437
+ except KeyError:
1438
+ return Object(wrapper)
1439
+
1440
+
1441
+ class SecretKey(types.SecretKey):
1442
+ pass
1443
+
1444
+
1445
+ class PublicKey(types.PublicKey):
1446
+ pass
1447
+
1448
+
1449
+ class PrivateKey(types.PrivateKey):
1450
+ pass
1451
+
1452
+
1453
+ class GenerateWithParametersMixin(types.DomainParameters):
1454
+ def generate_keypair(self,
1455
+ id=None, label=None,
1456
+ store=False, capabilities=None,
1457
+ mechanism=None, mechanism_param=None,
1458
+ public_template=None, private_template=None):
1459
+
1460
+ cdef Session session = self.session
1461
+ capabilities = _resolve_capabilities(self.key_type, capabilities)
1462
+
1463
+ mech = MechanismWithParam(self.key_type, DEFAULT_GENERATE_MECHANISMS, mechanism, mechanism_param)
1464
+
1465
+ # Build attributes
1466
+ public_template_ = session.attribute_mapper.public_key_template(
1467
+ id_=id, label=label, store=store, capabilities=capabilities,
1468
+ )
1469
+
1470
+ # Copy in our domain parameters.
1471
+ # Not all parameters are appropriate for all domains.
1472
+ try:
1473
+ public_template_.update(
1474
+ self.get_attributes(
1475
+ (
1476
+ Attribute.BASE,
1477
+ Attribute.PRIME,
1478
+ Attribute.SUBPRIME,
1479
+ Attribute.EC_PARAMS,
1480
+ )
1481
+ )
1482
+ )
1483
+ except (AttributeTypeInvalid, FunctionFailed):
1484
+ pass
1485
+
1486
+ public_attrs = session.make_attribute_list(merge_templates(public_template_, public_template))
1487
+
1488
+ private_template_ = session.attribute_mapper.private_key_template(
1489
+ id_=id, label=label, store=store, capabilities=capabilities,
1490
+ )
1491
+ private_attrs = session.make_attribute_list(merge_templates(private_template_, private_template))
1492
+
1493
+ return session.generate_keypair_from_attrs(public_attrs, private_attrs, mech)
1494
+
1495
+
1496
+ class LocalDomainParameters(GenerateWithParametersMixin, types.LocalDomainParameters):
1497
+ pass
1498
+
1499
+ class StoredDomainParameters(GenerateWithParametersMixin):
1500
+ pass
1501
+
1502
+ class Certificate(types.Certificate):
1503
+ pass
1504
+
1505
+
1506
+ cdef class KeyOperation(OperationWithBinaryOutput):
1507
+ cdef CK_OBJECT_HANDLE key
1508
+ cdef KeyOperationInit op_init
1509
+
1510
+ @staticmethod
1511
+ cdef KeyOperation _common_key_setup(
1512
+ type cls,
1513
+ Session session,
1514
+ MechanismWithParam mech,
1515
+ CK_OBJECT_HANDLE key,
1516
+ CK_ULONG buffer_size
1517
+ ) with gil:
1518
+
1519
+ cdef KeyOperation op = <KeyOperation> OperationWithBinaryOutput._setup(
1520
+ cls, session, mech, buffer_size
1521
+ )
1522
+ op.key = key
1523
+ return op
1524
+
1525
+ def unclean_shutdown(self):
1526
+ """
1527
+ Shutdown implementation for 2.x PKCS#11 modules that don't support shutdown signalling
1528
+ """
1529
+ raise NotImplementedError
1530
+
1531
+ def _initiate(self):
1532
+ cdef CK_RV retval
1533
+ with nogil:
1534
+ retval = self.op_init(self.session.handle, self.mech.data, self.key)
1535
+ self._operation_aware_assert(retval)
1536
+
1537
+ def _cancel_operation(self, silent):
1538
+ cdef CK_RV retval
1539
+ if self.session.cancel_strategy == CancelStrategy.CANCEL_WITH_INIT:
1540
+ # cancel the operation if still active
1541
+ # This is a PKCS#11 3.x feature
1542
+ with nogil:
1543
+ retval = self.op_init(self.session.handle, NULL, self.key)
1544
+ if retval == CKR_OPERATION_CANCEL_FAILED and not silent:
1545
+ raise PKCS11Error("Failed to cancel operation")
1546
+ else:
1547
+ # No official cancel protocol in v2.x of the standard
1548
+ # Try the poor man's way by making a hail-mary call to C_XYZFinish() and ignoring the response
1549
+ self.unclean_shutdown()
1550
+
1551
+ def _finalize(self, silent=False):
1552
+ if self.active:
1553
+ self._cancel_operation(silent)
1554
+ super()._finalize(silent=silent)
1555
+
1556
+
1557
+ cdef class DataCryptOperation(KeyOperation):
1558
+
1559
+ cdef OperationUpdateWithResult op_update
1560
+ cdef OperationWithResult op_final
1561
+ cdef OperationUpdateWithResult op_full
1562
+
1563
+ @staticmethod
1564
+ cdef DataCryptOperation setup_encrypt(
1565
+ Session session,
1566
+ MechanismWithParam mech,
1567
+ CK_OBJECT_HANDLE key,
1568
+ CK_ULONG buffer_size
1569
+ ) with gil:
1570
+ cdef DataCryptOperation op = <DataCryptOperation> KeyOperation._common_key_setup(
1571
+ DataCryptOperation, session, mech, key, buffer_size
1572
+ )
1573
+ op.op_init = session.funclist.C_EncryptInit
1574
+ op.op_update = session.funclist.C_EncryptUpdate
1575
+ op.op_final = session.funclist.C_EncryptFinal
1576
+ op.op_full = session.funclist.C_Encrypt
1577
+ return op
1578
+
1579
+ @staticmethod
1580
+ cdef DataCryptOperation setup_decrypt(
1581
+ Session session,
1582
+ MechanismWithParam mech,
1583
+ CK_OBJECT_HANDLE key,
1584
+ CK_ULONG buffer_size
1585
+ ) with gil:
1586
+ cdef DataCryptOperation op = <DataCryptOperation> KeyOperation._common_key_setup(
1587
+ DataCryptOperation, session, mech, key, buffer_size
1588
+ )
1589
+ op.op_init = session.funclist.C_DecryptInit
1590
+ op.op_update = session.funclist.C_DecryptUpdate
1591
+ op.op_final = session.funclist.C_DecryptFinal
1592
+ op.op_full = session.funclist.C_Decrypt
1593
+ return op
1594
+
1595
+ cdef bytes crypt_process_fully(self, CK_BYTE *data, CK_ULONG data_len) with gil:
1596
+ return self.process_fully(self.op_full, data, data_len)
1597
+
1598
+ cdef bytes finish(self) with gil:
1599
+ return self.finish_with_output(self.op_final)
1600
+
1601
+ def unclean_shutdown(self):
1602
+ self.execute_resizing_output(self.op_final)
1603
+
1604
+ def update_chunks(self, chunks):
1605
+ cdef CK_BYTE *data_ptr
1606
+ cdef CK_ULONG data_len
1607
+
1608
+ for chunk in chunks:
1609
+ if not chunk:
1610
+ continue
1611
+ data_ptr = chunk
1612
+ data_len = <CK_ULONG> len(chunk)
1613
+ yield self.update_with_result(self.op_update, data_ptr, data_len)
1614
+
1615
+
1616
+ class EncryptMixin(types.EncryptMixin):
1617
+ """Expand EncryptMixin with an implementation."""
1618
+
1619
+ def __encrypt_operation(self, mechanism, mechanism_param, buffer_size):
1620
+
1621
+ mech = MechanismWithParam(self.key_type, DEFAULT_ENCRYPT_MECHANISMS, mechanism, mechanism_param)
1622
+
1623
+ return DataCryptOperation.setup_encrypt(self.session, mech, self.handle, buffer_size)
1624
+
1625
+ def _encrypt(self, data, mechanism=None, mechanism_param=None, buffer_size=8192):
1626
+ """
1627
+ Non chunking encrypt. Needed for some mechanisms.
1628
+ """
1629
+ cdef CK_BYTE *data_ptr = data
1630
+ cdef CK_ULONG data_len = <CK_ULONG> len(data)
1631
+
1632
+ cdef DataCryptOperation op = self.__encrypt_operation(mechanism, mechanism_param, buffer_size)
1633
+ with op:
1634
+ return op.crypt_process_fully(data, data_len)
1635
+
1636
+
1637
+ def _encrypt_generator(self, data,
1638
+ mechanism=None, mechanism_param=None,
1639
+ buffer_size=8192):
1640
+ """
1641
+ Do chunked encryption.
1642
+ """
1643
+ cdef DataCryptOperation op = self.__encrypt_operation(mechanism, mechanism_param, buffer_size)
1644
+ with op:
1645
+ yield from op.update_chunks(data)
1646
+ yield op.finish()
1647
+
1648
+
1649
+ class DecryptMixin(types.DecryptMixin):
1650
+ """Expand DecryptMixin with an implementation."""
1651
+
1652
+ def __decrypt_operation(self, mechanism, mechanism_param, buffer_size):
1653
+
1654
+ mech = MechanismWithParam(self.key_type, DEFAULT_ENCRYPT_MECHANISMS, mechanism, mechanism_param)
1655
+
1656
+ return DataCryptOperation.setup_decrypt(self.session, mech, self.handle, buffer_size)
1657
+
1658
+ def _decrypt(self, data, mechanism=None, mechanism_param=None, pin=None, buffer_size=8192):
1659
+ """Non chunking decrypt."""
1660
+ cdef Session session = self.session
1661
+ cdef CK_BYTE *data_ptr = data
1662
+ cdef CK_ULONG data_len = <CK_ULONG> len(data)
1663
+
1664
+ cdef DataCryptOperation op = self.__decrypt_operation(mechanism, mechanism_param, buffer_size)
1665
+ with op:
1666
+ if pin is not None:
1667
+ session.reaffirm_credentials(pin)
1668
+ return op.crypt_process_fully(data, data_len)
1669
+
1670
+
1671
+ def _decrypt_generator(self, data,
1672
+ mechanism=None, mechanism_param=None, pin=None,
1673
+ buffer_size=8192):
1674
+ """
1675
+ Chunking decrypt.
1676
+ """
1677
+ cdef Session session = self.session
1678
+
1679
+ cdef DataCryptOperation op = self.__decrypt_operation(mechanism, mechanism_param, buffer_size)
1680
+ with op:
1681
+ if pin is not None:
1682
+ session.reaffirm_credentials(pin)
1683
+ yield from op.update_chunks(data)
1684
+ yield op.finish()
1685
+
1686
+
1687
+ cdef class SignOrVerifyOperation(KeyOperation):
1688
+ cdef OperationUpdate op_update
1689
+
1690
+ def ingest_chunks(self, chunks):
1691
+ cdef Session session = self.session
1692
+ cdef CK_BYTE *data_ptr
1693
+ cdef CK_ULONG data_len
1694
+
1695
+ for chunk in chunks:
1696
+ if not chunk:
1697
+ continue
1698
+ data_ptr = chunk
1699
+ data_len = <CK_ULONG> len(chunk)
1700
+ self.update_no_output(self.op_update, data_ptr, data_len)
1701
+
1702
+
1703
+ cdef class DataSignOperation(SignOrVerifyOperation):
1704
+
1705
+ @staticmethod
1706
+ cdef DataSignOperation setup(
1707
+ Session session,
1708
+ MechanismWithParam mech,
1709
+ CK_OBJECT_HANDLE key,
1710
+ CK_ULONG buffer_size
1711
+ ) with gil:
1712
+ cdef DataSignOperation op = <DataSignOperation> KeyOperation._common_key_setup(
1713
+ DataSignOperation, session, mech, key, buffer_size
1714
+ )
1715
+ op.op_init = session.funclist.C_SignInit
1716
+ op.op_update = session.funclist.C_SignUpdate
1717
+ return op
1718
+
1719
+ cdef bytes sign_process_fully(self, CK_BYTE *data, CK_ULONG data_len) with gil:
1720
+ cdef Session session = self.session
1721
+ return self.process_fully(session.funclist.C_Sign, data, data_len)
1722
+
1723
+ cdef bytes finish(self) with gil:
1724
+ cdef Session session = self.session
1725
+ return self.finish_with_output(session.funclist.C_SignFinal)
1726
+
1727
+ def unclean_shutdown(self):
1728
+ cdef Session session = self.session
1729
+ self.execute_resizing_output(session.funclist.C_SignFinal)
1730
+
1731
+
1732
+ class SignMixin(types.SignMixin):
1733
+ """Expand SignMixin with an implementation."""
1734
+
1735
+ def __sign_operation(self, mechanism, mechanism_param, buffer_size):
1736
+ mech = MechanismWithParam(self.key_type, DEFAULT_SIGN_MECHANISMS, mechanism, mechanism_param)
1737
+ return DataSignOperation.setup(self.session, mech, self.handle, buffer_size)
1738
+
1739
+ def _sign(self, data,
1740
+ mechanism=None, mechanism_param=None, pin=None, buffer_size=8192):
1741
+ cdef Session session = self.session
1742
+ cdef CK_BYTE *data_ptr = data
1743
+ cdef CK_ULONG data_len = <CK_ULONG> len(data)
1744
+ cdef DataSignOperation op = self.__sign_operation(mechanism, mechanism_param, buffer_size)
1745
+
1746
+ with op:
1747
+ if pin is not None:
1748
+ session.reaffirm_credentials(pin)
1749
+ return op.sign_process_fully(data, data_len)
1750
+
1751
+ def _sign_generator(self, data,
1752
+ mechanism=None, mechanism_param=None, pin=None, buffer_size=8192):
1753
+
1754
+ cdef Session session = self.session
1755
+ cdef DataSignOperation op = self.__sign_operation(mechanism, mechanism_param, buffer_size)
1756
+ with op:
1757
+ if pin is not None:
1758
+ session.reaffirm_credentials(pin)
1759
+ op.ingest_chunks(data)
1760
+ return op.finish()
1761
+
1762
+
1763
+ cdef class DataVerifyOperation(SignOrVerifyOperation):
1764
+
1765
+ @staticmethod
1766
+ cdef DataVerifyOperation setup(
1767
+ Session session,
1768
+ MechanismWithParam mech,
1769
+ CK_OBJECT_HANDLE key
1770
+ ) with gil:
1771
+ cdef DataVerifyOperation op = <DataVerifyOperation> KeyOperation._common_key_setup(
1772
+ DataVerifyOperation, session, mech, key, 0
1773
+ )
1774
+ op.op_init = session.funclist.C_VerifyInit
1775
+ op.op_update = session.funclist.C_VerifyUpdate
1776
+ return op
1777
+
1778
+ cdef verify_process_fully(
1779
+ self,
1780
+ CK_BYTE *data,
1781
+ CK_ULONG data_len,
1782
+ CK_BYTE *sig,
1783
+ CK_ULONG sig_len,
1784
+ ) with gil:
1785
+ cdef Session session = self.session
1786
+ cdef CK_RV retval
1787
+ with nogil:
1788
+ retval = session.funclist.C_Verify(session.handle, data, data_len, sig, sig_len)
1789
+ self._handle_final_retval(retval)
1790
+
1791
+ cdef finish(self, CK_BYTE *sig, CK_ULONG sig_len) with gil:
1792
+ cdef Session session = self.session
1793
+ cdef CK_RV retval
1794
+ with nogil:
1795
+ retval = session.funclist.C_VerifyFinal(session.handle, sig, sig_len)
1796
+ self._handle_final_retval(retval)
1797
+
1798
+ def unclean_shutdown(self):
1799
+ cdef Session session = self.session
1800
+ cdef CK_BYTE dummy = 0
1801
+ with nogil:
1802
+ session.funclist.C_VerifyFinal(session.handle, &dummy, 0)
1803
+
1804
+
1805
+ class VerifyMixin(types.VerifyMixin):
1806
+ """Expand VerifyMixin with an implementation."""
1807
+
1808
+ def __verify_operation(self, mechanism, mechanism_param):
1809
+ mech = MechanismWithParam(self.key_type, DEFAULT_SIGN_MECHANISMS, mechanism, mechanism_param)
1810
+ return DataVerifyOperation.setup(self.session, mech, self.handle)
1811
+
1812
+ def _verify(self, data, signature,
1813
+ mechanism=None, mechanism_param=None):
1814
+
1815
+ cdef CK_BYTE *data_ptr = data
1816
+ cdef CK_ULONG data_len = <CK_ULONG> len(data)
1817
+ cdef CK_BYTE *sig_ptr = signature
1818
+ cdef CK_ULONG sig_len = <CK_ULONG> len(signature)
1819
+ cdef DataVerifyOperation op = self.__verify_operation(mechanism, mechanism_param)
1820
+
1821
+ with op:
1822
+ op.verify_process_fully(data_ptr, data_len, sig_ptr, sig_len)
1823
+
1824
+ def _verify_generator(self, data, signature,
1825
+ mechanism=None, mechanism_param=None):
1826
+
1827
+ cdef CK_BYTE *sig_ptr = signature
1828
+ cdef CK_ULONG sig_len = <CK_ULONG> len(signature)
1829
+ cdef DataVerifyOperation op = self.__verify_operation(mechanism, mechanism_param)
1830
+
1831
+ with op:
1832
+ op.ingest_chunks(data)
1833
+ return op.finish(sig_ptr, sig_len)
1834
+
1835
+
1836
+ class WrapMixin(types.WrapMixin):
1837
+ """Expand WrapMixin with an implementation."""
1838
+
1839
+ def wrap_key(self, key,
1840
+ mechanism=None, mechanism_param=None):
1841
+
1842
+ if not isinstance(key, types.Key):
1843
+ raise ArgumentsBad("`key` must be a Key.")
1844
+
1845
+ mech = MechanismWithParam(self.key_type, DEFAULT_WRAP_MECHANISMS, mechanism, mechanism_param)
1846
+
1847
+ cdef Session session = self.session
1848
+ cdef CK_MECHANISM *mech_data = mech.data
1849
+ cdef CK_OBJECT_HANDLE wrapping_key = self.handle
1850
+ cdef CK_OBJECT_HANDLE key_to_wrap = key.handle
1851
+ cdef CK_ULONG length
1852
+ cdef CK_RV retval
1853
+
1854
+ # Find out how many bytes we need to allocate
1855
+ with nogil:
1856
+ retval = session.funclist.C_WrapKey(session.handle, mech_data, wrapping_key, key_to_wrap, NULL, &length)
1857
+ assertRV(retval)
1858
+
1859
+ cdef CK_BYTE [:] data = CK_BYTE_buffer(length)
1860
+
1861
+ with nogil:
1862
+ retval = session.funclist.C_WrapKey(session.handle, mech_data, wrapping_key, key_to_wrap, &data[0], &length)
1863
+ assertRV(retval)
1864
+
1865
+ return bytes(data[:length])
1866
+
1867
+
1868
+ class UnwrapMixin(types.UnwrapMixin):
1869
+ """Expand UnwrapMixin with an implementation."""
1870
+
1871
+ def unwrap_key(self, object_class, key_type, key_data,
1872
+ id=None, label=None,
1873
+ mechanism=None, mechanism_param=None,
1874
+ store=False, capabilities=None,
1875
+ template=None):
1876
+
1877
+ if not isinstance(object_class, ObjectClass):
1878
+ raise ArgumentsBad("`object_class` must be ObjectClass.")
1879
+
1880
+ _check_key_type(key_type)
1881
+ capabilities = _resolve_capabilities(key_type, capabilities)
1882
+
1883
+ mech = MechanismWithParam(self.key_type, DEFAULT_WRAP_MECHANISMS, mechanism, mechanism_param)
1884
+
1885
+ cdef Session session = self.session
1886
+
1887
+ # Build attributes
1888
+ template_ = session.attribute_mapper.generic_key_template(
1889
+ {
1890
+ Attribute.CLASS: object_class,
1891
+ Attribute.KEY_TYPE: key_type,
1892
+ },
1893
+ id_=id,
1894
+ label=label,
1895
+ store=store,
1896
+ capabilities=capabilities,
1897
+ )
1898
+ cdef AttributeList attrs = session.make_attribute_list(merge_templates(template_, template))
1899
+ cdef CK_MECHANISM *mech_data = mech.data
1900
+ cdef CK_OBJECT_HANDLE unwrapping_key = self.handle
1901
+ cdef CK_BYTE *wrapped_key_ptr = key_data
1902
+ cdef CK_ULONG wrapped_key_len = <CK_ULONG> len(key_data)
1903
+ cdef CK_ATTRIBUTE *attr_data = attrs.data
1904
+ cdef CK_ULONG attr_count = attrs.count
1905
+ cdef CK_OBJECT_HANDLE key
1906
+ cdef CK_RV retval
1907
+
1908
+ with nogil:
1909
+ retval = session.funclist.C_UnwrapKey(session.handle, mech_data, unwrapping_key, wrapped_key_ptr, wrapped_key_len, attr_data, attr_count, &key)
1910
+ assertRV(retval)
1911
+
1912
+ return make_object(session, key)
1913
+
1914
+
1915
+ class DeriveMixin(types.DeriveMixin):
1916
+ """Expand DeriveMixin with an implementation."""
1917
+
1918
+ def derive_key(self, key_type, key_length,
1919
+ id=None, label=None,
1920
+ store=False, capabilities=None,
1921
+ mechanism=None, mechanism_param=None,
1922
+ template=None):
1923
+
1924
+ _check_key_type(key_type)
1925
+ _check_bit_length(key_length, "key_length", required=True)
1926
+ capabilities = _resolve_capabilities(key_type, capabilities)
1927
+
1928
+ mech = MechanismWithParam(self.key_type, DEFAULT_DERIVE_MECHANISMS, mechanism, mechanism_param)
1929
+
1930
+ cdef Session session = self.session
1931
+
1932
+ template_ = session.attribute_mapper.secret_key_template(
1933
+ capabilities=capabilities, id_=id, label=label, store=store,
1934
+ )
1935
+ template_[Attribute.KEY_TYPE] = key_type
1936
+ template_[Attribute.VALUE_LEN] = key_length // 8 # In bytes
1937
+ cdef AttributeList attrs = session.make_attribute_list(merge_templates(template_, template))
1938
+ cdef CK_MECHANISM *mech_data = mech.data
1939
+ cdef CK_OBJECT_HANDLE src_key = self.handle
1940
+ cdef CK_ATTRIBUTE *attr_data = attrs.data
1941
+ cdef CK_ULONG attr_count = attrs.count
1942
+ cdef CK_OBJECT_HANDLE key
1943
+ cdef CK_RV retval
1944
+
1945
+ with nogil:
1946
+ retval = session.funclist.C_DeriveKey(session.handle, mech_data, src_key, attr_data, attr_count, &key)
1947
+ assertRV(retval)
1948
+
1949
+ return make_object(session, key)
1950
+
1951
+
1952
+ class EncapsulateMixin(types.EncapsulateMixin):
1953
+ """Expand EncapsulateMixin with an implementation (ML-KEM)."""
1954
+
1955
+ def encapsulate_key(self, key_type,
1956
+ key_length=None,
1957
+ id=None, label=None,
1958
+ store=False, capabilities=None,
1959
+ mechanism=None, mechanism_param=None,
1960
+ template=None):
1961
+
1962
+ _check_key_type(key_type)
1963
+ capabilities = _resolve_capabilities(key_type, capabilities)
1964
+
1965
+ mech = MechanismWithParam(self.key_type, DEFAULT_ENCAPSULATE_MECHANISMS, mechanism, mechanism_param)
1966
+
1967
+ cdef Session session = self.session
1968
+
1969
+ if session.funclist32 == NULL:
1970
+ raise FunctionNotSupported("C_EncapsulateKey requires a PKCS#11 v3.2+ library")
1971
+
1972
+ template_ = session.attribute_mapper.secret_key_template(
1973
+ capabilities=capabilities, id_=id, label=label, store=store,
1974
+ )
1975
+ if key_length is not None:
1976
+ template_[Attribute.VALUE_LEN] = key_length // 8
1977
+ template_[Attribute.KEY_TYPE] = key_type
1978
+ cdef AttributeList attrs = session.make_attribute_list(merge_templates(template_, template))
1979
+ cdef CK_MECHANISM *mech_data = mech.data
1980
+ cdef CK_OBJECT_HANDLE pub_key = self.handle
1981
+ cdef CK_ATTRIBUTE *attr_data = attrs.data
1982
+ cdef CK_ULONG attr_count = attrs.count
1983
+ cdef CK_ULONG ct_len = 0
1984
+ cdef CK_OBJECT_HANDLE ss_handle
1985
+ cdef CK_RV retval
1986
+
1987
+ with nogil:
1988
+ retval = session.funclist32.C_EncapsulateKey(
1989
+ session.handle, mech_data, pub_key,
1990
+ attr_data, attr_count, NULL, &ct_len, &ss_handle)
1991
+ assertRV(retval)
1992
+
1993
+ cdef CK_BYTE [:] ct_buf = CK_BYTE_buffer(ct_len)
1994
+
1995
+ with nogil:
1996
+ retval = session.funclist32.C_EncapsulateKey(
1997
+ session.handle, mech_data, pub_key,
1998
+ attr_data, attr_count, &ct_buf[0], &ct_len, &ss_handle)
1999
+ assertRV(retval)
2000
+
2001
+ return (bytes(ct_buf[:ct_len]), make_object(session, ss_handle))
2002
+
2003
+
2004
+ class DecapsulateMixin(types.DecapsulateMixin):
2005
+ """Expand DecapsulateMixin with an implementation (ML-KEM)."""
2006
+
2007
+ def decapsulate_key(self, ciphertext, key_type,
2008
+ key_length=None,
2009
+ id=None, label=None,
2010
+ store=False, capabilities=None,
2011
+ mechanism=None, mechanism_param=None,
2012
+ template=None):
2013
+
2014
+ _check_key_type(key_type)
2015
+ capabilities = _resolve_capabilities(key_type, capabilities)
2016
+
2017
+ mech = MechanismWithParam(self.key_type, DEFAULT_ENCAPSULATE_MECHANISMS, mechanism, mechanism_param)
2018
+
2019
+ cdef Session session = self.session
2020
+
2021
+ if session.funclist32 == NULL:
2022
+ raise FunctionNotSupported("C_DecapsulateKey requires a PKCS#11 v3.2+ library")
2023
+
2024
+ template_ = session.attribute_mapper.secret_key_template(
2025
+ capabilities=capabilities, id_=id, label=label, store=store,
2026
+ )
2027
+ if key_length is not None:
2028
+ template_[Attribute.VALUE_LEN] = key_length // 8
2029
+ template_[Attribute.KEY_TYPE] = key_type
2030
+ cdef AttributeList attrs = session.make_attribute_list(merge_templates(template_, template))
2031
+ cdef CK_MECHANISM *mech_data = mech.data
2032
+ cdef CK_OBJECT_HANDLE priv_key = self.handle
2033
+ cdef CK_BYTE *ct_ptr = ciphertext
2034
+ cdef CK_ULONG ct_len = <CK_ULONG> len(ciphertext)
2035
+ cdef CK_ATTRIBUTE *attr_data = attrs.data
2036
+ cdef CK_ULONG attr_count = attrs.count
2037
+ cdef CK_OBJECT_HANDLE key
2038
+ cdef CK_RV retval
2039
+
2040
+ with nogil:
2041
+ retval = session.funclist32.C_DecapsulateKey(
2042
+ session.handle, mech_data, priv_key,
2043
+ attr_data, attr_count, ct_ptr, ct_len, &key)
2044
+ assertRV(retval)
2045
+
2046
+ return make_object(session, key)
2047
+
2048
+
2049
+ _CLASS_MAP = {
2050
+ ObjectClass.SECRET_KEY: SecretKey,
2051
+ ObjectClass.PUBLIC_KEY: PublicKey,
2052
+ ObjectClass.PRIVATE_KEY: PrivateKey,
2053
+ ObjectClass.DOMAIN_PARAMETERS: StoredDomainParameters,
2054
+ ObjectClass.CERTIFICATE: Certificate,
2055
+ }
2056
+
2057
+ cdef extern from "../extern/load_module.c":
2058
+ ctypedef struct P11_HANDLE:
2059
+ void *get_function_list_ptr
2060
+ void *get_interface_ptr
2061
+
2062
+ object p11_error()
2063
+ P11_HANDLE* p11_open(object path_str)
2064
+ int p11_close(P11_HANDLE* handle)
2065
+
2066
+
2067
+ cdef class lib(HasFuncList):
2068
+ """
2069
+ Main entry point.
2070
+
2071
+ This class needs to be defined cdef, so it can't shadow a class in
2072
+ pkcs11.types.
2073
+ """
2074
+
2075
+ cdef readonly str so
2076
+ cdef readonly str manufacturer_id
2077
+ cdef readonly str library_description
2078
+ cdef readonly bint initialized
2079
+ cdef CK_VERSION _cryptoki_version
2080
+ cdef CK_VERSION _library_version
2081
+ cdef P11_HANDLE *_p11_handle
2082
+
2083
+ cdef _load_pkcs11_lib(self, so) with gil:
2084
+ """Load a PKCS#11 library, and extract function calls.
2085
+
2086
+ This method will dynamically load a PKCS11 library, and attempt to
2087
+ resolve the symbol 'C_GetFunctionList()'. Once found, the entry point
2088
+ is called to populate an internal table of function pointers.
2089
+
2090
+ This is a private method, and must never be called directly.
2091
+ Called when a new lib class is instantiated.
2092
+
2093
+ :param so: the path to a valid PKCS#11 library
2094
+ :type so: str
2095
+ :raises: PKCS11Error
2096
+ :rtype: None
2097
+ """
2098
+
2099
+ # to keep a pointer to the C_GetFunctionList address returned by dlsym()
2100
+ cdef C_GetFunctionList_ptr populate_function_list
2101
+ cdef CK_RV retval
2102
+
2103
+ cdef P11_HANDLE *handle = p11_open(so)
2104
+ if handle == NULL:
2105
+ err = <str> p11_error()
2106
+ if err:
2107
+ raise PKCS11Error(f"OS exception while loading {so}: {err}")
2108
+ else:
2109
+ raise PKCS11Error(f"Unknown exception while loading {so}")
2110
+ populate_function_list = <C_GetFunctionList_ptr> handle.get_function_list_ptr
2111
+ self._p11_handle = handle
2112
+
2113
+ assertRV(populate_function_list(&self.funclist))
2114
+
2115
+ def __cinit__(self, so):
2116
+ cdef CK_RV retval
2117
+ self._p11_handle = NULL
2118
+ self._load_pkcs11_lib(so)
2119
+ self.initialized = False
2120
+ # at this point, _funclist contains all function pointers to the library
2121
+
2122
+ cpdef initialize(self):
2123
+ cdef CK_RV retval
2124
+ if self.funclist != NULL and not self.initialized:
2125
+ with nogil:
2126
+ retval = self.funclist.C_Initialize(NULL)
2127
+ assertRV(retval)
2128
+ self.initialized = True
2129
+
2130
+ cdef CK_RV _finalize(self):
2131
+ cdef CK_RV retval = CKR_OK
2132
+ if self.funclist != NULL and self.initialized:
2133
+ retval = self.funclist.C_Finalize(NULL)
2134
+ self.initialized = False
2135
+ return retval
2136
+
2137
+ def finalize(self):
2138
+ assertRV(self._finalize())
2139
+
2140
+ def reinitialize(self):
2141
+ if self.funclist != NULL:
2142
+ self.finalize()
2143
+ self.initialize()
2144
+
2145
+ def __init__(self, so):
2146
+ self.so = so
2147
+ cdef CK_INFO info
2148
+ cdef CK_RV retval
2149
+
2150
+ self.initialize()
2151
+
2152
+ with nogil:
2153
+ retval = self.funclist.C_GetInfo(&info)
2154
+ assertRV(retval)
2155
+
2156
+ manufacturerID = info.manufacturerID[:sizeof(info.manufacturerID)]
2157
+ libraryDescription = info.libraryDescription[:sizeof(info.libraryDescription)]
2158
+
2159
+ self.manufacturer_id = _CK_UTF8CHAR_to_str(manufacturerID)
2160
+ self.library_description = _CK_UTF8CHAR_to_str(libraryDescription)
2161
+ self._cryptoki_version = info.cryptokiVersion
2162
+ self._library_version = info.libraryVersion
2163
+
2164
+ cdef C_GetInterface_ptr get_interface
2165
+ cdef CK_INTERFACE *iface
2166
+ cdef CK_VERSION ver
2167
+ if self._p11_handle.get_interface_ptr != NULL:
2168
+ get_interface = <C_GetInterface_ptr>self._p11_handle.get_interface_ptr
2169
+ ver.major = 3
2170
+ ver.minor = 0
2171
+ retval = get_interface(<CK_UTF8CHAR*>"PKCS 11", &ver, &iface, 0)
2172
+ if retval == CKR_OK and iface != NULL:
2173
+ self.funclist30 = <CK_FUNCTION_LIST_3_0*>iface.pFunctionList
2174
+ ver.major = 3
2175
+ ver.minor = 2
2176
+ retval = get_interface(<CK_UTF8CHAR*>"PKCS 11", &ver, &iface, 0)
2177
+ if retval == CKR_OK and iface != NULL:
2178
+ self.funclist32 = <CK_FUNCTION_LIST_3_2*>iface.pFunctionList
2179
+
2180
+ @property
2181
+ def library_version(self):
2182
+ """Hardware version (:class:`tuple`)."""
2183
+ return _CK_VERSION_to_tuple(self._library_version)
2184
+
2185
+ @property
2186
+ def cryptoki_version(self):
2187
+ """PKCS#11 (cryptoki) API version (:class:`tuple`)."""
2188
+ return _CK_VERSION_to_tuple(self._cryptoki_version)
2189
+
2190
+ def __str__(self):
2191
+ return '\n'.join((
2192
+ "Library: %s" % self.so,
2193
+ "Manufacturer ID: %s" % self.manufacturer_id,
2194
+ "Library Description: %s" % self.library_description,
2195
+ "Cryptoki Version: %s.%s" % self.cryptoki_version,
2196
+ "Library Version: %s.%s" % self.library_version,
2197
+ ))
2198
+
2199
+ def __repr__(self):
2200
+ return '<pkcs11.lib ({so})>'.format(
2201
+ so=self.so)
2202
+
2203
+
2204
+ def get_slots(self, token_present=False):
2205
+ """Get all slots."""
2206
+
2207
+ cdef CK_BBOOL present = token_present
2208
+ cdef CK_ULONG count
2209
+ cdef CK_RV retval
2210
+
2211
+ with nogil:
2212
+ retval = self.funclist.C_GetSlotList(present, NULL, &count)
2213
+ assertRV(retval)
2214
+
2215
+ if count == 0:
2216
+ return []
2217
+
2218
+ cdef CK_SLOT_ID [:] slot_list = CK_ULONG_buffer(count)
2219
+
2220
+ with nogil:
2221
+ retval = self.funclist.C_GetSlotList(present, &slot_list[0], &count)
2222
+ assertRV(retval)
2223
+
2224
+ cdef CK_SLOT_ID slot_id
2225
+ cdef CK_SLOT_INFO info
2226
+
2227
+ slots = []
2228
+
2229
+ for slot_id in slot_list:
2230
+ with nogil:
2231
+ retval = self.funclist.C_GetSlotInfo(slot_id, &info)
2232
+ assertRV(retval)
2233
+
2234
+ slot = Slot.make(self.funclist, slot_id, info, self._cryptoki_version)
2235
+ slot.funclist30 = self.funclist30
2236
+ slot.funclist32 = self.funclist32
2237
+ slots.append(slot)
2238
+
2239
+ return slots
2240
+
2241
+
2242
+ def get_tokens(self,
2243
+ token_label=None,
2244
+ token_serial=None,
2245
+ token_flags=None,
2246
+ slot_flags=None,
2247
+ mechanisms=None):
2248
+ """Search for a token matching the parameters."""
2249
+
2250
+ for slot in self.get_slots():
2251
+ try:
2252
+ token = slot.get_token()
2253
+ token_mechanisms = slot.get_mechanisms()
2254
+
2255
+ if token_label is not None and \
2256
+ token.label != token_label:
2257
+ continue
2258
+
2259
+ if token_serial is not None and \
2260
+ token.serial != token_serial:
2261
+ continue
2262
+
2263
+ if token_flags is not None and \
2264
+ not token.flags & token_flags:
2265
+ continue
2266
+
2267
+ if slot_flags is not None and \
2268
+ not slot.flags & slot_flags:
2269
+ continue
2270
+
2271
+ if mechanisms is not None and \
2272
+ set(mechanisms) not in token_mechanisms:
2273
+ continue
2274
+
2275
+ yield token
2276
+ except (TokenNotPresent, TokenNotRecognised):
2277
+ continue
2278
+
2279
+ def get_token(self, **kwargs):
2280
+ """Get a single token."""
2281
+ iterator = self.get_tokens(**kwargs)
2282
+
2283
+ try:
2284
+ token = next(iterator)
2285
+ except StopIteration:
2286
+ raise NoSuchToken("No token matching %s" % kwargs)
2287
+
2288
+ try:
2289
+ next(iterator)
2290
+ raise MultipleTokensReturned(
2291
+ "More than 1 token matches %s" % kwargs)
2292
+ except StopIteration:
2293
+ return token
2294
+
2295
+ def wait_for_slot_event(self, blocking=True):
2296
+ cdef CK_SLOT_ID slot_id
2297
+ cdef CK_FLAGS flag = 0
2298
+ cdef CK_RV retval
2299
+
2300
+ if not blocking:
2301
+ flag |= CKF_DONT_BLOCK
2302
+
2303
+ with nogil:
2304
+ retval = self.funclist.C_WaitForSlotEvent(flag, &slot_id, NULL)
2305
+ assertRV(retval)
2306
+
2307
+ cdef CK_SLOT_INFO info
2308
+
2309
+ with nogil:
2310
+ retval = self.funclist.C_GetSlotInfo(slot_id, &info)
2311
+ assertRV(retval)
2312
+
2313
+ slotDescription = info.slotDescription[:sizeof(info.slotDescription)]
2314
+ manufacturerID = info.manufacturerID[:sizeof(info.manufacturerID)]
2315
+
2316
+ return Slot(self, slot_id, slotDescription, manufacturerID,
2317
+ info.hardwareVersion, info.firmwareVersion, info.flags)
2318
+
2319
+ def unload(self):
2320
+ self._finalize()
2321
+ self.funclist = NULL
2322
+ if self._p11_handle != NULL:
2323
+ p11_close(self._p11_handle)
2324
+ self._p11_handle = NULL
2325
+
2326
+ def __dealloc__(self):
2327
+ self.unload()