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