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

LudovicRousseau / PyKCS11 / 16549303499

27 Jul 2025 08:49AM UTC coverage: 93.528% (+0.05%) from 93.474%
16549303499

push

github

LudovicRousseau
fix load(): move os.getenv() outside of the _lock scope

3 of 4 new or added lines in 1 file covered. (75.0%)

68 existing lines in 3 files now uncovered.

5636 of 6026 relevant lines covered (93.53%)

0.94 hits per line

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

86.4
/PyKCS11/__init__.py
1
"""
2
  Copyright (C) 2006-2025 Ludovic Rousseau (ludovic.rousseau@free.fr)
3
  Copyright (C) 2010 Giuseppe Amato (additions to original interface)
4

5
This file is free software; you can redistribute it and/or modify it
6
under the terms of the GNU General Public License as published by
7
the Free Software Foundation; either version 2 of the License, or
8
(at your option) any later version.
9

10
This program is distributed in the hope that it will be useful, but
11
WITHOUT ANY WARRANTY; without even the implied warranty of
12
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13
General Public License for more details.
14

15
You should have received a copy of the GNU General Public License
16
along with this program; if not, write to the Free Software
17
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
18
"""
19

20
# pylint: disable=too-many-lines
21

22
import os
1✔
23
import sys
1✔
24
import threading
1✔
25

26
import PyKCS11.LowLevel
1✔
27

28
from .constants import *
1✔
29

30
# special CKR[] values
31
CKR[-4] = "C_GetFunctionList() not found"
1✔
32
CKR[-3] = "Unknown format"
1✔
33
CKR[-2] = "Unkown PKCS#11 type"
1✔
34
CKR[-1] = "Load"
1✔
35

36

37
class ckbytelist(PyKCS11.LowLevel.ckbytelist):
1✔
38
    """
39
    add a __repr__() method to the LowLevel equivalent
40
    """
41

42
    def __init__(self, data=None):
1✔
43
        if data is None:
1✔
44
            data = 0
1✔
45
        elif isinstance(data, str):
1✔
46
            data = data.encode("utf-8")
1✔
47
        elif isinstance(data, (bytes, list, ckbytelist)):
1✔
48
            data = bytes(data)
1✔
49
        else:
50
            raise PyKCS11.PyKCS11Error(-3, text=str(type(data)))
1✔
51
        super().__init__(data)
1✔
52

53
    def __repr__(self):
1✔
54
        """
55
        return the representation of a tuple
56
        the __str__ method will use it also
57
        """
58
        rep = [int(elt) for elt in self]
1✔
59
        return repr(rep)
1✔
60

61
    def __add__(self, b):
1✔
62
        return ckbytelist(bytes(self) + bytes(b))
1✔
63

64

65
class CK_OBJECT_HANDLE(PyKCS11.LowLevel.CK_OBJECT_HANDLE):
1✔
66
    """
67
    add a __repr__() method to the LowLevel equivalent
68
    """
69

70
    def __init__(self, session):
1✔
71
        PyKCS11.LowLevel.CK_OBJECT_HANDLE.__init__(self)
1✔
72
        self.session = session
1✔
73

74
    def to_dict(self):
1✔
75
        """
76
        convert the fields of the object into a dictionnary
77
        """
78
        # all the attibutes defined by PKCS#11
79
        all_attributes = PyKCS11.CKA.keys()
1✔
80

81
        # only use the integer values and not the strings like 'CKM_RSA_PKCS'
82
        all_attributes = [attr for attr in all_attributes if isinstance(attr, int)]
1✔
83

84
        # all the attributes of the object
85
        attributes = self.session.getAttributeValue(self, all_attributes)
1✔
86

87
        dico = {}
1✔
88
        for key, attr in zip(all_attributes, attributes):
1✔
89
            if attr is None:
1✔
90
                continue
1✔
91
            if key == CKA_CLASS:
1✔
92
                dico[PyKCS11.CKA[key]] = PyKCS11.CKO[attr]
1✔
93
            elif key == CKA_CERTIFICATE_TYPE:
1✔
94
                dico[PyKCS11.CKA[key]] = PyKCS11.CKC[attr]
×
95
            elif key == CKA_KEY_TYPE:
1✔
96
                dico[PyKCS11.CKA[key]] = PyKCS11.CKK[attr]
1✔
97
            else:
98
                dico[PyKCS11.CKA[key]] = attr
1✔
99
        return dico
1✔
100

101
    def __repr__(self):
1✔
102
        """
103
        text representation of the object
104
        """
105
        dico = self.to_dict()
1✔
106
        lines = []
1✔
107
        for key in sorted(dico.keys()):
1✔
108
            lines.append(f"{key}: {dico[key]}")
1✔
109
        return "\n".join(lines)
1✔
110

111

112
class CkClass:
1✔
113
    """
114
    Base class for CK_* classes
115
    """
116

117
    # dictionnary of integer_value: text_value for the flags bits
118
    flags_dict = {}
1✔
119

120
    # dictionnary of fields names and types
121
    # type can be "pair", "flags" or "text"
122
    fields = {}
1✔
123

124
    flags = 0
1✔
125

126
    def flags2text(self):
1✔
127
        """
128
        parse the `self.flags` field and create a list of `CKF_*` strings
129
        corresponding to bits set in flags
130

131
        :return: a list of strings
132
        :rtype: list
133
        """
134
        r = []
1✔
135
        for k, v in self.flags_dict.items():
1✔
136
            if self.flags & k:
1✔
137
                r.append(v)
1✔
138
        return r
1✔
139

140
    def state2text(self):
1✔
141
        """
142
        Dummy method. Will be overwriden if necessary
143
        """
144
        return ""
×
145

146
    def to_dict(self):
1✔
147
        """
148
        convert the fields of the object into a dictionnary
149
        """
150
        dico = {}
1✔
151
        for field in self.fields:
1✔
152
            if field == "flags":
1✔
153
                dico[field] = self.flags2text()
1✔
154
            elif field == "state":
1✔
155
                dico[field] = self.state2text()
1✔
156
            else:
157
                dico[field] = self.__dict__[field]
1✔
158
        return dico
1✔
159

160
    def __str__(self):
1✔
161
        """
162
        text representation of the object
163
        """
164
        dico = self.to_dict()
1✔
165
        lines = []
1✔
166
        for key in sorted(dico.keys()):
1✔
167
            ck_type = self.fields[key]
1✔
168
            if ck_type == "flags":
1✔
169
                flags = ", ".join(dico[key])
1✔
170
                lines.append(f"{key}: {flags}")
1✔
171
            elif ck_type == "pair":
1✔
172
                p1, p2 = dico[key]
1✔
173
                lines.append(f"{key}: {p1}.{p2}")
1✔
174
            else:
175
                lines.append(f"{key}: {dico[key]}")
1✔
176
        return "\n".join(lines)
1✔
177

178

179
class CK_SLOT_INFO(CkClass):
1✔
180
    """
181
    matches the PKCS#11 CK_SLOT_INFO structure
182

183
    :ivar slotDescription: blank padded
184
    :type slotDescription: string
185
    :ivar manufacturerID: blank padded
186
    :type manufacturerID: string
187
    :ivar flags: See :func:`CkClass.flags2text`
188
    :type flags: integer
189
    :ivar hardwareVersion: 2 elements list
190
    :type hardwareVersion: list
191
    :ivar firmwareVersion: 2 elements list
192
    :type firmwareVersion: list
193
    """
194

195
    flags_dict = {
1✔
196
        CKF_TOKEN_PRESENT: "CKF_TOKEN_PRESENT",
197
        CKF_REMOVABLE_DEVICE: "CKF_REMOVABLE_DEVICE",
198
        CKF_HW_SLOT: "CKF_HW_SLOT",
199
    }
200

201
    fields = {
1✔
202
        "slotDescription": "text",
203
        "manufacturerID": "text",
204
        "flags": "flags",
205
        "hardwareVersion": "text",
206
        "firmwareVersion": "text",
207
    }
208

209
    def __init__(self):
1✔
210
        self.slotDescription = None
1✔
211
        self.manufacturerID = None
1✔
212
        self.flags = None
1✔
213
        self.hardwareVersion = None
1✔
214
        self.firmwareVersion = None
1✔
215

216

217
class CK_INFO(CkClass):
1✔
218
    """
219
    matches the PKCS#11 CK_INFO structure
220

221
    :ivar cryptokiVersion: Cryptoki interface version
222
    :type cryptokiVersion: integer
223
    :ivar manufacturerID: blank padded
224
    :type manufacturerID: string
225
    :ivar flags: must be zero
226
    :type flags: integer
227
    :ivar libraryDescription: blank padded
228
    :type libraryDescription: string
229
    :var libraryVersion: 2 elements list
230
    :type libraryVersion: list
231
    """
232

233
    fields = {
1✔
234
        "cryptokiVersion": "pair",
235
        "manufacturerID": "text",
236
        "flags": "flags",
237
        "libraryDescription": "text",
238
        "libraryVersion": "pair",
239
    }
240

241
    def __init__(self):
1✔
242
        self.cryptokiVersion = None
1✔
243
        self.manufacturerID = None
1✔
244
        self.flags = None
1✔
245
        self.libraryDescription = None
1✔
246
        self.libraryVersion = None
1✔
247

248

249
class CK_SESSION_INFO(CkClass):
1✔
250
    """
251
    matches the PKCS#11 CK_SESSION_INFO structure
252

253
    :ivar slotID: ID of the slot that interfaces with the token
254
    :type slotID: integer
255
    :ivar state: state of the session
256
    :type state: integer
257
    :ivar flags: bit flags that define the type of session
258
    :type flags: integer
259
    :ivar ulDeviceError: an error code defined by the cryptographic token
260
    :type ulDeviceError: integer
261
    """
262

263
    flags_dict = {
1✔
264
        CKF_RW_SESSION: "CKF_RW_SESSION",
265
        CKF_SERIAL_SESSION: "CKF_SERIAL_SESSION",
266
    }
267

268
    def __init__(self):
1✔
269
        self.slotID = None
1✔
270
        self.state = None
1✔
271
        self.flags = None
1✔
272
        self.ulDeviceError = None
1✔
273

274
    def state2text(self):
1✔
275
        """
276
        parse the `self.state` field and return a `CKS_*` string
277
        corresponding to the state
278

279
        :return: a string
280
        :rtype: string
281
        """
282
        return CKS[self.state]
1✔
283

284
    fields = {
1✔
285
        "slotID": "text",
286
        "state": "text",
287
        "flags": "flags",
288
        "ulDeviceError": "text",
289
    }
290

291

292
class CK_TOKEN_INFO(CkClass):
1✔
293
    """
294
    matches the PKCS#11 CK_TOKEN_INFO structure
295

296
    :ivar label: blank padded
297
    :type label: string
298
    :ivar manufacturerID: blank padded
299
    :type manufacturerID: string
300
    :ivar model: string blank padded
301
    :type model: string
302
    :ivar serialNumber: string blank padded
303
    :type serialNumber: string
304
    :ivar flags:
305
    :type flags: integer
306
    :ivar ulMaxSessionCount:
307
    :type ulMaxSessionCount: integer
308
    :ivar ulSessionCount:
309
    :type ulSessionCount: integer
310
    :ivar ulMaxRwSessionCount:
311
    :type ulMaxRwSessionCount: integer
312
    :ivar ulRwSessionCount:
313
    :type ulRwSessionCount: integer
314
    :ivar ulMaxPinLen:
315
    :type ulMaxPinLen: integer
316
    :ivar ulMinPinLen:
317
    :type ulMinPinLen: integer
318
    :ivar ulTotalPublicMemory:
319
    :type ulTotalPublicMemory: integer
320
    :ivar ulFreePublicMemory:
321
    :type ulFreePublicMemory: integer
322
    :ivar ulTotalPrivateMemory:
323
    :type ulTotalPrivateMemory: integer
324
    :ivar ulFreePrivateMemory:
325
    :type ulFreePrivateMemory: integer
326
    :ivar hardwareVersion: 2 elements list
327
    :type hardwareVersion: list
328
    :ivar firmwareVersion: 2 elements list
329
    :type firmwareVersion: list
330
    :ivar utcTime: string
331
    :type utcTime: string
332
    """
333

334
    # pylint: disable=too-many-instance-attributes
335

336
    flags_dict = {
1✔
337
        CKF_RNG: "CKF_RNG",
338
        CKF_WRITE_PROTECTED: "CKF_WRITE_PROTECTED",
339
        CKF_LOGIN_REQUIRED: "CKF_LOGIN_REQUIRED",
340
        CKF_USER_PIN_INITIALIZED: "CKF_USER_PIN_INITIALIZED",
341
        CKF_RESTORE_KEY_NOT_NEEDED: "CKF_RESTORE_KEY_NOT_NEEDED",
342
        CKF_CLOCK_ON_TOKEN: "CKF_CLOCK_ON_TOKEN",
343
        CKF_PROTECTED_AUTHENTICATION_PATH: "CKF_PROTECTED_AUTHENTICATION_PATH",
344
        CKF_DUAL_CRYPTO_OPERATIONS: "CKF_DUAL_CRYPTO_OPERATIONS",
345
        CKF_TOKEN_INITIALIZED: "CKF_TOKEN_INITIALIZED",
346
        CKF_SECONDARY_AUTHENTICATION: "CKF_SECONDARY_AUTHENTICATION",
347
        CKF_USER_PIN_COUNT_LOW: "CKF_USER_PIN_COUNT_LOW",
348
        CKF_USER_PIN_FINAL_TRY: "CKF_USER_PIN_FINAL_TRY",
349
        CKF_USER_PIN_LOCKED: "CKF_USER_PIN_LOCKED",
350
        CKF_USER_PIN_TO_BE_CHANGED: "CKF_USER_PIN_TO_BE_CHANGED",
351
        CKF_SO_PIN_COUNT_LOW: "CKF_SO_PIN_COUNT_LOW",
352
        CKF_SO_PIN_FINAL_TRY: "CKF_SO_PIN_FINAL_TRY",
353
        CKF_SO_PIN_LOCKED: "CKF_SO_PIN_LOCKED",
354
        CKF_SO_PIN_TO_BE_CHANGED: "CKF_SO_PIN_TO_BE_CHANGED",
355
    }
356

357
    fields = {
1✔
358
        "label": "text",
359
        "manufacturerID": "text",
360
        "model": "text",
361
        "serialNumber": "text",
362
        "flags": "flags",
363
        "ulMaxSessionCount": "text",
364
        "ulSessionCount": "text",
365
        "ulMaxRwSessionCount": "text",
366
        "ulRwSessionCount": "text",
367
        "ulMaxPinLen": "text",
368
        "ulMinPinLen": "text",
369
        "ulTotalPublicMemory": "text",
370
        "ulFreePublicMemory": "text",
371
        "ulTotalPrivateMemory": "text",
372
        "ulFreePrivateMemory": "text",
373
        "hardwareVersion": "pair",
374
        "firmwareVersion": "pair",
375
        "utcTime": "text",
376
    }
377

378
    def __init__(self):
1✔
379
        self.label = None
1✔
380
        self.manufacturerID = None
1✔
381
        self.model = None
1✔
382
        self.serialNumber = None
1✔
383
        self.flags = None
1✔
384
        self.ulMaxSessionCount = None
1✔
385
        self.ulSessionCount = None
1✔
386
        self.ulMaxRwSessionCount = None
1✔
387
        self.ulRwSessionCount = None
1✔
388
        self.ulMaxPinLen = None
1✔
389
        self.ulMinPinLen = None
1✔
390
        self.ulTotalPublicMemory = None
1✔
391
        self.ulFreePublicMemory = None
1✔
392
        self.ulTotalPrivateMemory = None
1✔
393
        self.ulFreePrivateMemory = None
1✔
394
        self.hardwareVersion = None
1✔
395
        self.firmwareVersion = None
1✔
396
        self.utcTime = None
1✔
397

398

399
class CK_MECHANISM_INFO(CkClass):
1✔
400
    """
401
    matches the PKCS#11 CK_MECHANISM_INFO structure
402

403
    :ivar ulMinKeySize: minimum size of the key
404
    :type ulMinKeySize: integer
405
    :ivar ulMaxKeySize: maximum size of the key
406
    :type ulMaxKeySize: integer
407
    :ivar flags: bit flags specifying mechanism capabilities
408
    :type flags: integer
409
    """
410

411
    flags_dict = {
1✔
412
        CKF_HW: "CKF_HW",
413
        CKF_ENCRYPT: "CKF_ENCRYPT",
414
        CKF_DECRYPT: "CKF_DECRYPT",
415
        CKF_DIGEST: "CKF_DIGEST",
416
        CKF_SIGN: "CKF_SIGN",
417
        CKF_SIGN_RECOVER: "CKF_SIGN_RECOVER",
418
        CKF_VERIFY: "CKF_VERIFY",
419
        CKF_VERIFY_RECOVER: "CKF_VERIFY_RECOVER",
420
        CKF_GENERATE: "CKF_GENERATE",
421
        CKF_GENERATE_KEY_PAIR: "CKF_GENERATE_KEY_PAIR",
422
        CKF_WRAP: "CKF_WRAP",
423
        CKF_UNWRAP: "CKF_UNWRAP",
424
        CKF_DERIVE: "CKF_DERIVE",
425
        CKF_EXTENSION: "CKF_EXTENSION",
426
    }
427

428
    fields = {"ulMinKeySize": "text", "ulMaxKeySize": "text", "flags": "flags"}
1✔
429

430
    def __init__(self):
1✔
431
        self.ulMinKeySize = None
1✔
432
        self.ulMaxKeySize = None
1✔
433
        self.flags = None
1✔
434

435

436
class PyKCS11Error(Exception):
1✔
437
    """define the possible PyKCS11 exceptions"""
438

439
    def __init__(self, value, text=""):
1✔
440
        self.value = value
1✔
441
        self.text = text
1✔
442

443
    def __str__(self):
1✔
444
        """
445
        The text representation of a PKCS#11 error is something like:
446
        "CKR_DEVICE_ERROR (0x00000030)"
447
        """
448
        if self.value in CKR:
1✔
449
            if self.value < 0:
1✔
450
                return CKR[self.value] + f" ({self.text})"
1✔
451
            return CKR[self.value] + f" (0x{self.value:08X})"
1✔
452
        if self.value & CKR_VENDOR_DEFINED:
1✔
453
            v = self.value & 0xFFFFFFFF & ~CKR_VENDOR_DEFINED
1✔
454
            return f"Vendor error (0x{v:08X})"
1✔
455
        return f"Unknown error (0x{self.value:08X})"
1✔
456

457

458
class PyKCS11Lib:
1✔
459
    """high level PKCS#11 binding"""
460

461
    # shared by all instances
462
    _loaded_libs = {}
1✔
463
    _lock = threading.Lock()
1✔
464

465
    def __init__(self):
1✔
466
        self.lib = PyKCS11.LowLevel.CPKCS11Lib()
1✔
467
        self.pkcs11dll_filename = None
1✔
468

469
    def __del__(self):
1✔
470
        # pylint: disable=too-many-boolean-expressions
471
        if (
1✔
472
            PyKCS11
473
            and PyKCS11.__name__
474
            and PyKCS11.LowLevel
475
            and PyKCS11.LowLevel.__name__
476
            and PyKCS11.LowLevel._LowLevel
477
            and PyKCS11.LowLevel._LowLevel.__name__
478
        ):
479

480
            # unload the library
481
            self.unload()
1✔
482

483
    def load(self, pkcs11dll_filename=None):
1✔
484
        """
485
        load a PKCS#11 library
486

487
        :type pkcs11dll_filename: string
488
        :param pkcs11dll_filename: the library name.
489
          If this parameter is not set then the environment variable
490
          `PYKCS11LIB` is used instead
491
        :returns: a :class:`PyKCS11Lib` object
492
        :raises: :class:`PyKCS11Error` (-1): when the load fails
493
        """
494
        if pkcs11dll_filename is None:
1✔
495
            pkcs11dll_filename = os.getenv("PYKCS11LIB")
1✔
496
            if pkcs11dll_filename is None:
1✔
NEW
497
                raise PyKCS11Error(
×
498
                    -1, "No PKCS11 library specified (set PYKCS11LIB env variable)"
499
                )
500

501
        with PyKCS11Lib._lock:
1✔
502
            if self.pkcs11dll_filename is not None:
1✔
503
                self._unload_locked()  # unload the previous library
1✔
504
                # if the instance was previously initialized,
505
                # create a new low level library object for it
506
                self.lib = PyKCS11.LowLevel.CPKCS11Lib()
1✔
507

508
            # if the lib is already in use: reuse it
509
            if pkcs11dll_filename in PyKCS11Lib._loaded_libs:
1✔
510
                self.lib.Duplicate(PyKCS11Lib._loaded_libs[pkcs11dll_filename]["ref"])
1✔
511
            else:
512
                # else load it
513
                rv = self.lib.Load(pkcs11dll_filename)
1✔
514
                if rv != CKR_OK:
1✔
515
                    raise PyKCS11Error(rv, pkcs11dll_filename)
1✔
516
                PyKCS11Lib._loaded_libs[pkcs11dll_filename] = {
1✔
517
                    "ref": self.lib,
518
                    "nb_users": 0,
519
                }
520

521
            # remember the lib file name
522
            self.pkcs11dll_filename = pkcs11dll_filename
1✔
523

524
            # increase user number
525
            PyKCS11Lib._loaded_libs[pkcs11dll_filename]["nb_users"] += 1
1✔
526

527
        return self
1✔
528

529
    def unload(self):
1✔
530
        """
531
        unload the current instance of a PKCS#11 library
532
        """
533
        with PyKCS11Lib._lock:
1✔
534
            self._unload_locked()
1✔
535

536
    def _unload_locked(self):
1✔
537
        """
538
        unload the current instance of a PKCS#11 library
539
        The lock is already held
540
        """
541

542
        # in case NO library was found and used
543
        if self.pkcs11dll_filename is None:
1✔
544
            return
1✔
545

546
        if self.pkcs11dll_filename not in PyKCS11Lib._loaded_libs:
1✔
547
            raise PyKCS11Error(
×
548
                -1,
549
                f"invalid PyKCS11Lib state: {self.pkcs11dll_filename} "
550
                + f"not in {PyKCS11Lib._loaded_libs}",
551
            )
552

553
        # decrease user number
554
        PyKCS11Lib._loaded_libs[self.pkcs11dll_filename]["nb_users"] -= 1
1✔
555

556
        if PyKCS11Lib._loaded_libs[self.pkcs11dll_filename]["nb_users"] == 0:
1✔
557
            # unload only if no more used
558
            self.lib.Unload()
1✔
559

560
        # remove unused entry
561
        # the case < 0 happens if lib loading failed
562
        if PyKCS11Lib._loaded_libs[self.pkcs11dll_filename]["nb_users"] <= 0:
1✔
563
            del PyKCS11Lib._loaded_libs[self.pkcs11dll_filename]
1✔
564

565
        self.pkcs11dll_filename = None
1✔
566

567
    def initToken(self, slot, pin, label):
1✔
568
        """
569
        C_InitToken
570

571
        :param slot: slot number returned by :func:`getSlotList`
572
        :type slot: integer
573
        :param pin: Security Officer's initial PIN
574
        :param label: new label of the token
575
        """
576
        pin1 = ckbytelist(pin)
1✔
577
        rv = self.lib.C_InitToken(slot, pin1, label)
1✔
578
        if rv != CKR_OK:
1✔
579
            raise PyKCS11Error(rv)
×
580

581
    def getInfo(self):
1✔
582
        """
583
        C_GetInfo
584

585
        :return: a :class:`CK_INFO` object
586
        """
587
        info = PyKCS11.LowLevel.CK_INFO()
1✔
588
        rv = self.lib.C_GetInfo(info)
1✔
589
        if rv != CKR_OK:
1✔
590
            raise PyKCS11Error(rv)
×
591

592
        i = CK_INFO()
1✔
593
        i.cryptokiVersion = (info.cryptokiVersion.major, info.cryptokiVersion.minor)
1✔
594
        i.manufacturerID = info.GetManufacturerID()
1✔
595
        i.flags = info.flags
1✔
596
        i.libraryDescription = info.GetLibraryDescription()
1✔
597
        i.libraryVersion = (info.libraryVersion.major, info.libraryVersion.minor)
1✔
598
        return i
1✔
599

600
    def getSlotList(self, tokenPresent=False):
1✔
601
        """
602
        C_GetSlotList
603

604
        :param tokenPresent: `False` (default) to list all slots,
605
          `True` to list only slots with present tokens
606
        :type tokenPresent: bool
607
        :return: a list of available slots
608
        :rtype: list
609
        """
610
        slotList = PyKCS11.LowLevel.ckulonglist()
1✔
611
        rv = self.lib.C_GetSlotList(CK_TRUE if tokenPresent else CK_FALSE, slotList)
1✔
612
        if rv != CKR_OK:
1✔
613
            raise PyKCS11Error(rv)
×
614

615
        s = []
1✔
616
        for x in slotList:
1✔
617
            s.append(x)
1✔
618
        return s
1✔
619

620
    def getSlotInfo(self, slot):
1✔
621
        """
622
        C_GetSlotInfo
623

624
        :param slot: slot number returned by :func:`getSlotList`
625
        :type slot: integer
626
        :return: a :class:`CK_SLOT_INFO` object
627
        """
628
        slotInfo = PyKCS11.LowLevel.CK_SLOT_INFO()
1✔
629
        rv = self.lib.C_GetSlotInfo(slot, slotInfo)
1✔
630
        if rv != CKR_OK:
1✔
631
            raise PyKCS11Error(rv)
×
632

633
        s = CK_SLOT_INFO()
1✔
634
        s.slotDescription = slotInfo.GetSlotDescription()
1✔
635
        s.manufacturerID = slotInfo.GetManufacturerID()
1✔
636
        s.flags = slotInfo.flags
1✔
637
        s.hardwareVersion = slotInfo.GetHardwareVersion()
1✔
638
        s.firmwareVersion = slotInfo.GetFirmwareVersion()
1✔
639

640
        return s
1✔
641

642
    def getTokenInfo(self, slot):
1✔
643
        """
644
        C_GetTokenInfo
645

646
        :param slot: slot number returned by :func:`getSlotList`
647
        :type slot: integer
648
        :return: a :class:`CK_TOKEN_INFO` object
649
        """
650
        tokeninfo = PyKCS11.LowLevel.CK_TOKEN_INFO()
1✔
651
        rv = self.lib.C_GetTokenInfo(slot, tokeninfo)
1✔
652
        if rv != CKR_OK:
1✔
653
            raise PyKCS11Error(rv)
×
654

655
        t = CK_TOKEN_INFO()
1✔
656
        t.label = tokeninfo.GetLabel()
1✔
657
        t.manufacturerID = tokeninfo.GetManufacturerID()
1✔
658
        t.model = tokeninfo.GetModel()
1✔
659
        t.serialNumber = tokeninfo.GetSerialNumber()
1✔
660
        t.flags = tokeninfo.flags
1✔
661
        t.ulMaxSessionCount = tokeninfo.ulMaxSessionCount
1✔
662
        if t.ulMaxSessionCount == CK_UNAVAILABLE_INFORMATION:
1✔
663
            t.ulMaxSessionCount = -1
×
664
        t.ulSessionCount = tokeninfo.ulSessionCount
1✔
665
        if t.ulSessionCount == CK_UNAVAILABLE_INFORMATION:
1✔
666
            t.ulSessionCount = -1
1✔
667
        t.ulMaxRwSessionCount = tokeninfo.ulMaxRwSessionCount
1✔
668
        if t.ulMaxRwSessionCount == CK_UNAVAILABLE_INFORMATION:
1✔
669
            t.ulMaxRwSessionCount = -1
×
670
        t.ulRwSessionCount = tokeninfo.ulRwSessionCount
1✔
671
        if t.ulRwSessionCount == CK_UNAVAILABLE_INFORMATION:
1✔
672
            t.ulRwSessionCount = -1
1✔
673
        t.ulMaxPinLen = tokeninfo.ulMaxPinLen
1✔
674
        t.ulMinPinLen = tokeninfo.ulMinPinLen
1✔
675
        t.ulTotalPublicMemory = tokeninfo.ulTotalPublicMemory
1✔
676

677
        if t.ulTotalPublicMemory == CK_UNAVAILABLE_INFORMATION:
1✔
678
            t.ulTotalPublicMemory = -1
1✔
679
        t.ulFreePublicMemory = tokeninfo.ulFreePublicMemory
1✔
680
        if t.ulFreePublicMemory == CK_UNAVAILABLE_INFORMATION:
1✔
681
            t.ulFreePublicMemory = -1
1✔
682
        t.ulTotalPrivateMemory = tokeninfo.ulTotalPrivateMemory
1✔
683
        if t.ulTotalPrivateMemory == CK_UNAVAILABLE_INFORMATION:
1✔
684
            t.ulTotalPrivateMemory = -1
1✔
685
        t.ulFreePrivateMemory = tokeninfo.ulFreePrivateMemory
1✔
686
        if t.ulFreePrivateMemory == CK_UNAVAILABLE_INFORMATION:
1✔
687
            t.ulFreePrivateMemory = -1
1✔
688
        t.hardwareVersion = (
1✔
689
            tokeninfo.hardwareVersion.major,
690
            tokeninfo.hardwareVersion.minor,
691
        )
692
        t.firmwareVersion = (
1✔
693
            tokeninfo.firmwareVersion.major,
694
            tokeninfo.firmwareVersion.minor,
695
        )
696
        t.utcTime = tokeninfo.GetUtcTime().replace("\000", " ")
1✔
697

698
        return t
1✔
699

700
    def openSession(self, slot, flags=0):
1✔
701
        """
702
        C_OpenSession
703

704
        :param slot: slot number returned by :func:`getSlotList`
705
        :type slot: integer
706
        :param flags: 0 (default), `CKF_RW_SESSION` for RW session
707
        :type flags: integer
708
        :return: a :class:`Session` object
709
        """
710
        se = PyKCS11.LowLevel.CK_SESSION_HANDLE()
1✔
711
        flags |= CKF_SERIAL_SESSION
1✔
712
        rv = self.lib.C_OpenSession(slot, flags, se)
1✔
713
        if rv != CKR_OK:
1✔
714
            raise PyKCS11Error(rv)
×
715

716
        return Session(self, se)
1✔
717

718
    def closeAllSessions(self, slot):
1✔
719
        """
720
        C_CloseAllSessions
721

722
        :param slot: slot number
723
        :type slot: integer
724
        """
725
        rv = self.lib.C_CloseAllSessions(slot)
1✔
726
        if rv != CKR_OK:
1✔
727
            raise PyKCS11Error(rv)
×
728

729
    def getMechanismList(self, slot):
1✔
730
        """
731
        C_GetMechanismList
732

733
        :param slot: slot number returned by :func:`getSlotList`
734
        :type slot: integer
735
        :return: the list of available mechanisms for a slot
736
        :rtype: list
737
        """
738
        mechanismList = PyKCS11.LowLevel.ckulonglist()
1✔
739
        rv = self.lib.C_GetMechanismList(slot, mechanismList)
1✔
740
        if rv != CKR_OK:
1✔
741
            raise PyKCS11Error(rv)
×
742

743
        m = []
1✔
744
        for mechanism in mechanismList:
1✔
745
            if mechanism >= CKM_VENDOR_DEFINED:
1✔
746
                mecha = mechanism - CKM_VENDOR_DEFINED
×
747
                k = f"CKM_VENDOR_DEFINED_0x{mecha:X}"
×
748
                CKM[k] = mechanism
×
749
                CKM[mechanism] = k
×
750
            m.append(CKM[mechanism])
1✔
751
        return m
1✔
752

753
    def getMechanismInfo(self, slot, ckm_type):
1✔
754
        """
755
        C_GetMechanismInfo
756

757
        :param slot: slot number returned by :func:`getSlotList`
758
        :type slot: integer
759
        :param ckm_type: a `CKM_*` type
760
        :type ckm_type: integer
761
        :return: information about a mechanism
762
        :rtype: a :class:`CK_MECHANISM_INFO` object
763
        """
764
        info = PyKCS11.LowLevel.CK_MECHANISM_INFO()
1✔
765
        rv = self.lib.C_GetMechanismInfo(slot, CKM[ckm_type], info)
1✔
766
        if rv != CKR_OK:
1✔
767
            raise PyKCS11Error(rv)
×
768

769
        i = CK_MECHANISM_INFO()
1✔
770
        i.ulMinKeySize = info.ulMinKeySize
1✔
771
        i.ulMaxKeySize = info.ulMaxKeySize
1✔
772
        i.flags = info.flags
1✔
773

774
        return i
1✔
775

776
    def waitForSlotEvent(self, flags=0):
1✔
777
        """
778
        C_WaitForSlotEvent
779

780
        :param flags: 0 (default) or `CKF_DONT_BLOCK`
781
        :type flags: integer
782
        :return: slot
783
        :rtype: integer
784
        """
785
        tmp = 0
×
786
        (rv, slot) = self.lib.C_WaitForSlotEvent(flags, tmp)
×
787
        if rv != CKR_OK:
×
788
            raise PyKCS11Error(rv)
×
789

790
        return slot
×
791

792

793
class Mechanism:
1✔
794
    """Wraps CK_MECHANISM"""
795

796
    # pylint: disable=too-few-public-methods
797

798
    def __init__(self, mechanism, param=None):
1✔
799
        """
800
        :param mechanism: the mechanism to be used
801
        :type mechanism: integer, any `CKM_*` value
802
        :param param: data to be used as crypto operation parameter
803
          (i.e. the IV for some algorithms)
804
        :type param: string or list/tuple of bytes
805

806
        :see: :func:`Session.decrypt`, :func:`Session.sign`
807
        """
808
        self._mech = PyKCS11.LowLevel.CK_MECHANISM()
1✔
809
        self._mech.mechanism = mechanism
1✔
810
        self._param = None
1✔
811
        if param:
1✔
812
            self._param = ckbytelist(param)
1✔
813
            self._mech.pParameter = self._param
1✔
814
            self._mech.ulParameterLen = len(param)
1✔
815

816
    def to_native(self):
1✔
817
        """convert mechanism to native format"""
818
        return self._mech
1✔
819

820

821
MechanismSHA1 = Mechanism(CKM_SHA_1, None)
1✔
822
MechanismRSAPKCS1 = Mechanism(CKM_RSA_PKCS, None)
1✔
823
MechanismRSAGENERATEKEYPAIR = Mechanism(CKM_RSA_PKCS_KEY_PAIR_GEN, None)
1✔
824
MechanismECGENERATEKEYPAIR = Mechanism(CKM_EC_KEY_PAIR_GEN, None)
1✔
825
MechanismAESGENERATEKEY = Mechanism(CKM_AES_KEY_GEN, None)
1✔
826

827

828
class AES_GCM_Mechanism:
1✔
829
    """CKM_AES_GCM warpping mechanism"""
830

831
    # pylint: disable=too-few-public-methods
832

833
    def __init__(self, iv, aad, tagBits):
1✔
834
        """
835
        :param iv: initialization vector
836
        :param aad: additional authentication data
837
        :param tagBits: length of authentication tag in bits
838
        """
839
        self._param = PyKCS11.LowLevel.CK_GCM_PARAMS()
1✔
840

841
        self._source_iv = ckbytelist(iv)
1✔
842
        self._param.pIv = self._source_iv
1✔
843
        self._param.ulIvLen = len(self._source_iv)
1✔
844

845
        self._source_aad = ckbytelist(aad)
1✔
846
        self._param.pAAD = self._source_aad
1✔
847
        self._param.ulAADLen = len(self._source_aad)
1✔
848

849
        self._param.ulTagBits = tagBits
1✔
850

851
        self._mech = PyKCS11.LowLevel.CK_MECHANISM()
1✔
852
        self._mech.mechanism = CKM_AES_GCM
1✔
853
        self._mech.pParameter = self._param
1✔
854
        self._mech.ulParameterLen = PyKCS11.LowLevel.CK_GCM_PARAMS_LENGTH
1✔
855

856
    def to_native(self):
1✔
857
        """convert mechanism to native format"""
858
        return self._mech
1✔
859

860

861
class AES_CTR_Mechanism:
1✔
862
    """CKM_AES_CTR encryption mechanism"""
863

864
    # pylint: disable=too-few-public-methods
865

866
    def __init__(self, counterBits, counterBlock):
1✔
867
        """
868
        :param counterBits: the number of incremented bits in the counter block
869
        :param counterBlock: a 16-byte initial value of the counter block
870
        """
871
        self._param = PyKCS11.LowLevel.CK_AES_CTR_PARAMS()
1✔
872

873
        self._source_cb = ckbytelist(counterBlock)
1✔
874
        self._param.ulCounterBits = counterBits
1✔
875
        self._param.cb = self._source_cb
1✔
876

877
        self._mech = PyKCS11.LowLevel.CK_MECHANISM()
1✔
878
        self._mech.mechanism = CKM_AES_CTR
1✔
879
        self._mech.pParameter = self._param
1✔
880
        self._mech.ulParameterLen = PyKCS11.LowLevel.CK_AES_CTR_PARAMS_LENGTH
1✔
881

882
    def to_native(self):
1✔
883
        """convert mechanism to native format"""
884
        return self._mech
1✔
885

886

887
class RSAOAEPMechanism:
1✔
888
    """RSA OAEP Wrapping mechanism"""
889

890
    # pylint: disable=too-few-public-methods
891

892
    def __init__(self, hashAlg, mgf, label=None):
1✔
893
        """
894
        :param hashAlg: the hash algorithm to use (like `CKM_SHA256`)
895
        :param mgf: the mask generation function to use (like
896
          `CKG_MGF1_SHA256`)
897
        :param label: the (optional) label to use
898
        """
899
        self._param = PyKCS11.LowLevel.CK_RSA_PKCS_OAEP_PARAMS()
1✔
900
        self._param.hashAlg = hashAlg
1✔
901
        self._param.mgf = mgf
1✔
902
        self._source = None
1✔
903
        self._param.source = CKZ_DATA_SPECIFIED
1✔
904
        if label:
1✔
905
            self._source = ckbytelist(label)
×
906
            self._param.ulSourceDataLen = len(self._source)
×
907
        else:
908
            self._param.ulSourceDataLen = 0
1✔
909
        self._param.pSourceData = self._source
1✔
910
        self._mech = PyKCS11.LowLevel.CK_MECHANISM()
1✔
911
        self._mech.mechanism = CKM_RSA_PKCS_OAEP
1✔
912
        self._mech.pParameter = self._param
1✔
913
        self._mech.ulParameterLen = PyKCS11.LowLevel.CK_RSA_PKCS_OAEP_PARAMS_LENGTH
1✔
914

915
    def to_native(self):
1✔
916
        """convert mechanism to native format"""
917
        return self._mech
1✔
918

919

920
class RSA_PSS_Mechanism:
1✔
921
    """RSA PSS Wrapping mechanism"""
922

923
    # pylint: disable=too-few-public-methods
924

925
    def __init__(self, mecha, hashAlg, mgf, sLen):
1✔
926
        """
927
        :param mecha: the mechanism to use (like
928
          `CKM_SHA384_RSA_PKCS_PSS`)
929
        :param hashAlg: the hash algorithm to use (like `CKM_SHA384`)
930
        :param mgf: the mask generation function to use (like
931
          `CKG_MGF1_SHA384`)
932
        :param sLen: length, in bytes, of the salt value used in the PSS
933
          encoding (like 0 or the message length)
934
        """
935
        self._param = PyKCS11.LowLevel.CK_RSA_PKCS_PSS_PARAMS()
1✔
936
        self._param.hashAlg = hashAlg
1✔
937
        self._param.mgf = mgf
1✔
938
        self._param.sLen = sLen
1✔
939
        self._mech = PyKCS11.LowLevel.CK_MECHANISM()
1✔
940
        self._mech.mechanism = mecha
1✔
941
        self._mech.pParameter = self._param
1✔
942
        self._mech.ulParameterLen = PyKCS11.LowLevel.CK_RSA_PKCS_PSS_PARAMS_LENGTH
1✔
943

944
    def to_native(self):
1✔
945
        """convert mechanism to native format"""
946
        return self._mech
1✔
947

948

949
class ECDH1_DERIVE_Mechanism:
1✔
950
    """CKM_ECDH1_DERIVE key derivation mechanism"""
951

952
    # pylint: disable=too-few-public-methods
953

954
    def __init__(self, publicData, kdf=CKD_NULL, sharedData=None):
1✔
955
        """
956
        :param publicData: Other party public key which is EC Point [PC || coord-x || coord-y].
957
        :param kdf: Key derivation function. OPTIONAL. Defaults to CKD_NULL
958
        :param sharedData: additional shared data. OPTIONAL
959
        """
960
        self._param = PyKCS11.LowLevel.CK_ECDH1_DERIVE_PARAMS()
1✔
961

962
        self._param.kdf = kdf
1✔
963

964
        if sharedData:
1✔
965
            self._shared_data = ckbytelist(sharedData)
×
966
            self._param.pSharedData = self._shared_data
×
967
            self._param.ulSharedDataLen = len(self._shared_data)
×
968
        else:
969
            self._source_shared_data = None
1✔
970
            self._param.ulSharedDataLen = 0
1✔
971

972
        self._public_data = ckbytelist(publicData)
1✔
973
        self._param.pPublicData = self._public_data
1✔
974
        self._param.ulPublicDataLen = len(self._public_data)
1✔
975

976
        self._mech = PyKCS11.LowLevel.CK_MECHANISM()
1✔
977
        self._mech.mechanism = CKM_ECDH1_DERIVE
1✔
978
        self._mech.pParameter = self._param
1✔
979
        self._mech.ulParameterLen = PyKCS11.LowLevel.CK_ECDH1_DERIVE_PARAMS_LENGTH
1✔
980

981
    def to_native(self):
1✔
982
        """convert mechanism to native format"""
983
        return self._mech
1✔
984

985

986
class CONCATENATE_BASE_AND_KEY_Mechanism:
1✔
987
    """CKM_CONCATENATE_BASE_AND_KEY key derivation mechanism"""
988

989
    # pylint: disable=too-few-public-methods
990

991
    def __init__(self, encKey):
1✔
992
        """
993
        :param encKey: a handle of encryption key
994
        """
995
        self._encKey = encKey
×
996

997
        self._mech = PyKCS11.LowLevel.CK_MECHANISM()
×
998
        self._mech.mechanism = CKM_CONCATENATE_BASE_AND_KEY
×
999
        self._mech.pParameter = self._encKey
×
1000
        self._mech.ulParameterLen = PyKCS11.LowLevel.CK_OBJECT_HANDLE_LENGTH
×
1001

1002
    def to_native(self):
1✔
1003
        """convert mechanism to native format"""
1004
        return self._mech
×
1005

1006

1007
class KEY_DERIVATION_STRING_DATA_MechanismBase:
1✔
1008
    """Base class for mechanisms using derivation string data"""
1009

1010
    # pylint: disable=too-few-public-methods
1011

1012
    def __init__(self, data, mechType):
1✔
1013
        """
1014
        :param data: a byte array to concatenate the key with
1015
        :param mechType: mechanism type
1016
        """
1017
        self._param = PyKCS11.LowLevel.CK_KEY_DERIVATION_STRING_DATA()
×
1018

1019
        self._data = ckbytelist(data)
×
1020
        self._param.pData = self._data
×
1021
        self._param.ulLen = len(self._data)
×
1022

1023
        self._mech = PyKCS11.LowLevel.CK_MECHANISM()
×
1024
        self._mech.mechanism = mechType
×
1025
        self._mech.pParameter = self._param
×
1026
        self._mech.ulParameterLen = (
×
1027
            PyKCS11.LowLevel.CK_KEY_DERIVATION_STRING_DATA_LENGTH
1028
        )
1029

1030
    def to_native(self):
1✔
1031
        """convert mechanism to native format"""
1032
        return self._mech
×
1033

1034

1035
class CONCATENATE_BASE_AND_DATA_Mechanism(KEY_DERIVATION_STRING_DATA_MechanismBase):
1✔
1036
    """CKM_CONCATENATE_BASE_AND_DATA key derivation mechanism"""
1037

1038
    # pylint: disable=too-few-public-methods
1039

1040
    def __init__(self, data):
1✔
1041
        """
1042
        :param data: a byte array to concatenate the key with
1043
        """
1044
        super().__init__(data, CKM_CONCATENATE_BASE_AND_DATA)
×
1045

1046

1047
class CONCATENATE_DATA_AND_BASE_Mechanism(KEY_DERIVATION_STRING_DATA_MechanismBase):
1✔
1048
    """CKM_CONCATENATE_DATA_AND_BASE key derivation mechanism"""
1049

1050
    # pylint: disable=too-few-public-methods
1051

1052
    def __init__(self, data):
1✔
1053
        """
1054
        :param data: a byte array to concatenate the key with
1055
        """
1056
        super().__init__(data, CKM_CONCATENATE_DATA_AND_BASE)
×
1057

1058

1059
class XOR_BASE_AND_DATA_Mechanism(KEY_DERIVATION_STRING_DATA_MechanismBase):
1✔
1060
    """CKM_XOR_BASE_AND_DATA key derivation mechanism"""
1061

1062
    # pylint: disable=too-few-public-methods
1063

1064
    def __init__(self, data):
1✔
1065
        """
1066
        :param data: a byte array to xor the key with
1067
        """
1068
        super().__init__(data, CKM_XOR_BASE_AND_DATA)
×
1069

1070

1071
class EXTRACT_KEY_FROM_KEY_Mechanism:
1✔
1072
    """CKM_EXTRACT_KEY_FROM_KEY key derivation mechanism"""
1073

1074
    # pylint: disable=too-few-public-methods
1075

1076
    def __init__(self, extractParams):
1✔
1077
        """
1078
        :param extractParams: the index of the first bit of the original
1079
        key to be used in the newly-derived key.  For example if
1080
        extractParams=5 then the 5 first bits are skipped and not used.
1081
        """
1082
        self._param = PyKCS11.LowLevel.CK_EXTRACT_PARAMS()
×
1083
        self._param.assign(extractParams)
×
1084

1085
        self._mech = PyKCS11.LowLevel.CK_MECHANISM()
×
1086
        self._mech.mechanism = CKM_EXTRACT_KEY_FROM_KEY
×
1087
        self._mech.pParameter = self._param
×
1088
        self._mech.ulParameterLen = PyKCS11.LowLevel.CK_EXTRACT_PARAMS_LENGTH
×
1089

1090
    def to_native(self):
1✔
1091
        """convert mechanism to native format"""
1092
        return self._mech
×
1093

1094

1095
class EDDSA_Mechanism:
1✔
1096
    """CKM_EDDSA signature mechanism"""
1097

1098
    # pylint: disable=too-few-public-methods
1099

1100
    def __init__(self, phFlag=None, contextData=None):
1✔
1101
        """
1102
        :param phFlag: prehash flag [True|False]. If this parameter is not set,
1103
        Ed25519 in pure mode without context is assumed.
1104
        :param context: context data (optional)
1105
        """
1106
        self._param = PyKCS11.LowLevel.CK_EDDSA_PARAMS()
1✔
1107
        self._mech = PyKCS11.LowLevel.CK_MECHANISM()
1✔
1108
        self._mech.mechanism = CKM_EDDSA
1✔
1109

1110
        if not phFlag is None:
1✔
1111
            self._phFlag = phFlag
1✔
1112
            self._param.phFlag = self._phFlag
1✔
1113

1114
            if contextData:
1✔
1115
                self._contextData = ckbytelist(contextData)
1✔
1116
                self._param.pContextData = self._contextData
1✔
1117
                self._param.ulContextDataLen = len(self._contextData)
1✔
1118

1119
            self._mech.pParameter = self._param
1✔
1120
            self._mech.ulParameterLen = PyKCS11.LowLevel.CK_EDDSA_PARAMS_LENGTH
1✔
1121

1122
    def to_native(self):
1✔
1123
        """convert mechanism to native format"""
1124
        return self._mech
1✔
1125

1126

1127
class DigestSession:
1✔
1128
    """Digest session"""
1129

1130
    def __init__(self, lib, session, mecha):
1✔
1131
        self._lib = lib
1✔
1132
        self._session = session
1✔
1133
        self._mechanism = mecha.to_native()
1✔
1134
        rv = self._lib.C_DigestInit(self._session, self._mechanism)
1✔
1135
        if rv != CKR_OK:
1✔
UNCOV
1136
            raise PyKCS11Error(rv)
×
1137

1138
    def update(self, data):
1✔
1139
        """
1140
        C_DigestUpdate
1141

1142
        :param data: data to add to the digest
1143
        :type data: bytes or string
1144
        """
1145
        data1 = ckbytelist(data)
1✔
1146
        rv = self._lib.C_DigestUpdate(self._session, data1)
1✔
1147
        if rv != CKR_OK:
1✔
UNCOV
1148
            raise PyKCS11Error(rv)
×
1149
        return self
1✔
1150

1151
    def digestKey(self, handle):
1✔
1152
        """
1153
        C_DigestKey
1154

1155
        :param handle: key handle
1156
        :type handle: CK_OBJECT_HANDLE
1157
        """
UNCOV
1158
        rv = self._lib.C_DigestKey(self._session, handle)
×
UNCOV
1159
        if rv != CKR_OK:
×
UNCOV
1160
            raise PyKCS11Error(rv)
×
UNCOV
1161
        return self
×
1162

1163
    def final(self):
1✔
1164
        """
1165
        C_DigestFinal
1166

1167
        :return: the digest
1168
        :rtype: ckbytelist
1169
        """
1170
        digest = ckbytelist()
1✔
1171
        # Get the size of the digest
1172
        rv = self._lib.C_DigestFinal(self._session, digest)
1✔
1173
        if rv != CKR_OK:
1✔
UNCOV
1174
            raise PyKCS11Error(rv)
×
1175
        # Get the actual digest
1176
        rv = self._lib.C_DigestFinal(self._session, digest)
1✔
1177
        if rv != CKR_OK:
1✔
UNCOV
1178
            raise PyKCS11Error(rv)
×
1179
        return digest
1✔
1180

1181

1182
class Session:
1✔
1183
    """Manage :func:`PyKCS11Lib.openSession` objects"""
1184

1185
    # pylint: disable=too-many-public-methods
1186

1187
    def __init__(self, pykcs11, session):
1✔
1188
        """
1189
        :param pykcs11: PyKCS11 library object
1190
        :type pykcs11: PyKCS11Lib
1191
        :param session: session handle
1192
        :type session: instance of :class:`CK_SESSION_HANDLE`
1193
        """
1194
        if not isinstance(pykcs11, PyKCS11Lib):
1✔
1195
            raise TypeError("pykcs11 must be a PyKCS11Lib")
×
1196
        if not isinstance(session, PyKCS11.LowLevel.CK_SESSION_HANDLE):
1✔
UNCOV
1197
            raise TypeError("session must be a CK_SESSION_HANDLE")
×
1198

1199
        # hold the PyKCS11Lib reference, so that it's not Garbage Collection'd
1200
        self.pykcs11 = pykcs11
1✔
1201
        self.session = session
1✔
1202

1203
    @property
1✔
1204
    def lib(self):
1✔
1205
        """
1206
        Get the low level lib of the owning PyKCS11Lib
1207
        """
1208
        return self.pykcs11.lib
1✔
1209

1210
    def closeSession(self):
1✔
1211
        """
1212
        C_CloseSession
1213
        """
1214
        rv = self.lib.C_CloseSession(self.session)
1✔
1215
        if rv != CKR_OK:
1✔
UNCOV
1216
            raise PyKCS11Error(rv)
×
1217

1218
    def getSessionInfo(self):
1✔
1219
        """
1220
        C_GetSessionInfo
1221

1222
        :return: a :class:`CK_SESSION_INFO` object
1223
        """
1224
        sessioninfo = PyKCS11.LowLevel.CK_SESSION_INFO()
1✔
1225
        rv = self.lib.C_GetSessionInfo(self.session, sessioninfo)
1✔
1226
        if rv != CKR_OK:
1✔
UNCOV
1227
            raise PyKCS11Error(rv)
×
1228

1229
        s = CK_SESSION_INFO()
1✔
1230
        s.slotID = sessioninfo.slotID
1✔
1231
        s.state = sessioninfo.state
1✔
1232
        s.flags = sessioninfo.flags
1✔
1233
        s.ulDeviceError = sessioninfo.ulDeviceError
1✔
1234
        return s
1✔
1235

1236
    def login(self, pin, user_type=CKU_USER):
1✔
1237
        """
1238
        C_Login
1239

1240
        :param pin: the user's PIN or None for CKF_PROTECTED_AUTHENTICATION_PATH
1241
        :type pin: string
1242
        :param user_type: the user type. The default value is
1243
          CKU_USER. You may also use CKU_SO
1244
        :type user_type: integer
1245
        """
1246
        pin1 = ckbytelist(pin)
1✔
1247
        rv = self.lib.C_Login(self.session, user_type, pin1)
1✔
1248
        if rv != CKR_OK:
1✔
1249
            raise PyKCS11Error(rv)
1✔
1250

1251
    def logout(self):
1✔
1252
        """
1253
        C_Logout
1254
        """
1255
        rv = self.lib.C_Logout(self.session)
1✔
1256
        if rv != CKR_OK:
1✔
UNCOV
1257
            raise PyKCS11Error(rv)
×
1258

1259
        del self
1✔
1260

1261
    def initPin(self, pin):
1✔
1262
        """
1263
        C_InitPIN
1264

1265
        :param pin: new PIN
1266
        """
1267
        new_pin1 = ckbytelist(pin)
1✔
1268
        rv = self.lib.C_InitPIN(self.session, new_pin1)
1✔
1269
        if rv != CKR_OK:
1✔
UNCOV
1270
            raise PyKCS11Error(rv)
×
1271

1272
    def setPin(self, old_pin, new_pin):
1✔
1273
        """
1274
        C_SetPIN
1275

1276
        :param old_pin: old PIN
1277
        :param new_pin: new PIN
1278
        """
1279
        old_pin1 = ckbytelist(old_pin)
1✔
1280
        new_pin1 = ckbytelist(new_pin)
1✔
1281
        rv = self.lib.C_SetPIN(self.session, old_pin1, new_pin1)
1✔
1282
        if rv != CKR_OK:
1✔
UNCOV
1283
            raise PyKCS11Error(rv)
×
1284

1285
    def createObject(self, template):
1✔
1286
        """
1287
        C_CreateObject
1288

1289
        :param template: object template
1290
        """
1291
        attrs = self._template2ckattrlist(template)
1✔
1292
        handle = PyKCS11.LowLevel.CK_OBJECT_HANDLE()
1✔
1293
        rv = self.lib.C_CreateObject(self.session, attrs, handle)
1✔
1294
        if rv != PyKCS11.CKR_OK:
1✔
UNCOV
1295
            raise PyKCS11.PyKCS11Error(rv)
×
1296
        return handle
1✔
1297

1298
    def destroyObject(self, obj):
1✔
1299
        """
1300
        C_DestroyObject
1301

1302
        :param obj: object ID
1303
        """
1304
        rv = self.lib.C_DestroyObject(self.session, obj)
1✔
1305
        if rv != CKR_OK:
1✔
UNCOV
1306
            raise PyKCS11Error(rv)
×
1307

1308
    def digestSession(self, mecha=MechanismSHA1):
1✔
1309
        """
1310
        C_DigestInit/C_DigestUpdate/C_DigestKey/C_DigestFinal
1311

1312
        :param mecha: the digesting mechanism to be used
1313
          (use `MechanismSHA1` for `CKM_SHA_1`)
1314
        :type mecha: :class:`Mechanism`
1315
        :return: A :class:`DigestSession` object
1316
        :rtype: DigestSession
1317
        """
1318
        return DigestSession(self.lib, self.session, mecha)
1✔
1319

1320
    def digest(self, data, mecha=MechanismSHA1):
1✔
1321
        """
1322
        C_DigestInit/C_Digest
1323

1324
        :param data: the data to be digested
1325
        :type data:  (binary) sring or list/tuple of bytes
1326
        :param mecha: the digesting mechanism to be used
1327
          (use `MechanismSHA1` for `CKM_SHA_1`)
1328
        :type mecha: :class:`Mechanism`
1329
        :return: the computed digest
1330
        :rtype: ckbytelist
1331

1332
        :note: the returned value is an istance of :class:`ckbytelist`.
1333
          You can easly convert it to a binary string with:
1334
          ``bytes(ckbytelistDigest)``
1335
          or, for Python 2:
1336
          ``''.join(chr(i) for i in ckbytelistDigest)``
1337

1338
        """
1339
        digest = ckbytelist()
1✔
1340
        m = mecha.to_native()
1✔
1341
        data1 = ckbytelist(data)
1✔
1342
        rv = self.lib.C_DigestInit(self.session, m)
1✔
1343
        if rv != CKR_OK:
1✔
UNCOV
1344
            raise PyKCS11Error(rv)
×
1345
        # first call get digest size
1346
        rv = self.lib.C_Digest(self.session, data1, digest)
1✔
1347
        if rv != CKR_OK:
1✔
UNCOV
1348
            raise PyKCS11Error(rv)
×
1349
        # second call get actual digest data
1350
        rv = self.lib.C_Digest(self.session, data1, digest)
1✔
1351
        if rv != CKR_OK:
1✔
UNCOV
1352
            raise PyKCS11Error(rv)
×
1353
        return digest
1✔
1354

1355
    def sign(self, key, data, mecha=MechanismRSAPKCS1):
1✔
1356
        """
1357
        C_SignInit/C_Sign
1358

1359
        :param key: a key handle, obtained calling :func:`findObjects`.
1360
        :type key: PyKCS11.LowLevel.CK_OBJECT_HANDLE
1361
        :param data: the data to be signed
1362
        :type data:  (binary) string or list/tuple of bytes
1363
        :param mecha: the signing mechanism to be used
1364
          (use `MechanismRSAPKCS1` for `CKM_RSA_PKCS`)
1365
        :type mecha: :class:`Mechanism`
1366
        :return: the computed signature
1367
        :rtype: ckbytelist
1368

1369
        :note: the returned value is an instance of :class:`ckbytelist`.
1370
          You can easly convert it to a binary string with:
1371
          ``bytes(ckbytelistSignature)``
1372
          or, for Python 2:
1373
          ``''.join(chr(i) for i in ckbytelistSignature)``
1374

1375
        """
1376
        m = mecha.to_native()
1✔
1377
        signature = ckbytelist()
1✔
1378
        data1 = ckbytelist(data)
1✔
1379
        rv = self.lib.C_SignInit(self.session, m, key)
1✔
1380
        if rv != CKR_OK:
1✔
1381
            raise PyKCS11Error(rv)
×
1382
        # first call get signature size
1383
        rv = self.lib.C_Sign(self.session, data1, signature)
1✔
1384
        if rv != CKR_OK:
1✔
1385
            raise PyKCS11Error(rv)
×
1386
        # second call get actual signature data
1387
        rv = self.lib.C_Sign(self.session, data1, signature)
1✔
1388
        if rv != CKR_OK:
1✔
UNCOV
1389
            raise PyKCS11Error(rv)
×
1390
        return signature
1✔
1391

1392
    def verify(self, key, data, signature, mecha=MechanismRSAPKCS1):
1✔
1393
        """
1394
        C_VerifyInit/C_Verify
1395

1396
        :param key: a key handle, obtained calling :func:`findObjects`.
1397
        :type key: PyKCS11.LowLevel.CK_OBJECT_HANDLE
1398
        :param data: the data that was signed
1399
        :type data:  (binary) string or list/tuple of bytes
1400
        :param signature: the signature to be verified
1401
        :type signature:  (binary) string or list/tuple of bytes
1402
        :param mecha: the signing mechanism to be used
1403
          (use `MechanismRSAPKCS1` for `CKM_RSA_PKCS`)
1404
        :type mecha: :class:`Mechanism`
1405
        :return: True if signature is valid, False otherwise
1406
        :rtype: bool
1407

1408
        """
1409
        m = mecha.to_native()
1✔
1410
        data1 = ckbytelist(data)
1✔
1411
        rv = self.lib.C_VerifyInit(self.session, m, key)
1✔
1412
        if rv != CKR_OK:
1✔
UNCOV
1413
            raise PyKCS11Error(rv)
×
1414
        rv = self.lib.C_Verify(self.session, data1, signature)
1✔
1415
        if rv == CKR_OK:
1✔
1416
            return True
1✔
UNCOV
1417
        if rv == CKR_SIGNATURE_INVALID:
×
UNCOV
1418
            return False
×
1419
        raise PyKCS11Error(rv)
×
1420

1421
    def encrypt(self, key, data, mecha=MechanismRSAPKCS1):
1✔
1422
        """
1423
        C_EncryptInit/C_Encrypt
1424

1425
        :param key: a key handle, obtained calling :func:`findObjects`.
1426
        :type key: PyKCS11.LowLevel.CK_OBJECT_HANDLE
1427
        :param data: the data to be encrypted
1428
        :type data:  (binary) string or list/tuple of bytes
1429
        :param mecha: the encryption mechanism to be used
1430
          (use `MechanismRSAPKCS1` for `CKM_RSA_PKCS`)
1431
        :type mecha: :class:`Mechanism`
1432
        :return: the encrypted data
1433
        :rtype: ckbytelist
1434

1435
        :note: the returned value is an instance of :class:`ckbytelist`.
1436
          You can easly convert it to a binary string with:
1437
          ``bytes(ckbytelistEncrypted)``
1438
          or, for Python 2:
1439
          ``''.join(chr(i) for i in ckbytelistEncrypted)``
1440

1441
        """
1442
        encrypted = ckbytelist()
1✔
1443
        m = mecha.to_native()
1✔
1444
        data1 = ckbytelist(data)
1✔
1445
        rv = self.lib.C_EncryptInit(self.session, m, key)
1✔
1446
        if rv != CKR_OK:
1✔
UNCOV
1447
            raise PyKCS11Error(rv)
×
1448
        # first call get encrypted size
1449
        rv = self.lib.C_Encrypt(self.session, data1, encrypted)
1✔
1450
        if rv != CKR_OK:
1✔
1451
            raise PyKCS11Error(rv)
×
1452
        # second call get actual encrypted data
1453
        rv = self.lib.C_Encrypt(self.session, data1, encrypted)
1✔
1454
        if rv != CKR_OK:
1✔
UNCOV
1455
            raise PyKCS11Error(rv)
×
1456
        return encrypted
1✔
1457

1458
    def encryptInit(self, mech, key):
1✔
1459
        """
1460
        C_EncryptInit
1461

1462
        :param mech: the encryption mechanism to be used
1463
        :type mech: instance of :class:`Mechanism`
1464
        :param key: a key handle
1465
        :type key: integer
1466
        """
1467
        m = mech.to_native()
1✔
1468
        rv = self.lib.C_EncryptInit(self.session, m, key)
1✔
1469
        if rv != CKR_OK:
1✔
UNCOV
1470
            raise PyKCS11Error(rv)
×
1471

1472
    def encryptUpdate(self, data):
1✔
1473
        """
1474
        C_EncryptUpdate
1475

1476
        :param data: the data to be encrypted
1477
        :type data: (binary) string or list/tuple of bytes
1478
        """
1479
        encrypted = ckbytelist()
1✔
1480
        data1 = ckbytelist(data)
1✔
1481
        rv = self.lib.C_EncryptUpdate(self.session, data1, encrypted)
1✔
1482
        if rv != CKR_OK:
1✔
UNCOV
1483
            raise PyKCS11Error(rv)
×
1484
        return encrypted
1✔
1485

1486
    def encryptFinal(self):
1✔
1487
        """
1488
        C_EncryptFinal
1489

1490
        :return: the last part of data to be encrypted
1491
        :rtype: (binary) string or list/tuple of bytes
1492
        """
1493
        encrypted = ckbytelist()
1✔
1494
        rv = self.lib.C_EncryptFinal(self.session, encrypted)
1✔
1495
        if rv != CKR_OK:
1✔
UNCOV
1496
            raise PyKCS11Error(rv)
×
1497
        return encrypted
1✔
1498

1499
    def decrypt(self, key, data, mecha=MechanismRSAPKCS1):
1✔
1500
        """
1501
        C_DecryptInit/C_Decrypt
1502

1503
        :param key: a key handle, obtained calling :func:`findObjects`.
1504
        :type key: PyKCS11.LowLevel.CK_OBJECT_HANDLE
1505
        :param data: the data to be decrypted
1506
        :type data:  (binary) string or list/tuple of bytes
1507
        :param mecha: the decrypt mechanism to be used
1508
        :type mecha: :class:`Mechanism` instance or :class:`MechanismRSAPKCS1`
1509
          for CKM_RSA_PKCS
1510
        :return: the decrypted data
1511
        :rtype: ckbytelist
1512

1513
        :note: the returned value is an instance of :class:`ckbytelist`.
1514
          You can easly convert it to a binary string with:
1515
          ``bytes(ckbytelistData)``
1516
          or, for Python 2:
1517
          ``''.join(chr(i) for i in ckbytelistData)``
1518

1519
        """
1520
        m = mecha.to_native()
1✔
1521
        decrypted = ckbytelist()
1✔
1522
        data1 = ckbytelist(data)
1✔
1523
        rv = self.lib.C_DecryptInit(self.session, m, key)
1✔
1524
        if rv != CKR_OK:
1✔
UNCOV
1525
            raise PyKCS11Error(rv)
×
1526
        # first call get decrypted size
1527
        rv = self.lib.C_Decrypt(self.session, data1, decrypted)
1✔
1528
        if rv != CKR_OK:
1✔
1529
            raise PyKCS11Error(rv)
×
1530
        # second call get actual decrypted data
1531
        rv = self.lib.C_Decrypt(self.session, data1, decrypted)
1✔
1532
        if rv != CKR_OK:
1✔
UNCOV
1533
            raise PyKCS11Error(rv)
×
1534
        return decrypted
1✔
1535

1536
    def decryptInit(self, mech, key):
1✔
1537
        """
1538
        C_DecryptInit
1539

1540
        :param mech: the decrypt mechanism to be used
1541
        :type mech: instance of :class:`Mechanism`
1542
        :param key: a key handle
1543
        :type key: integer
1544
        """
1545
        m = mech.to_native()
1✔
1546
        rv = self.lib.C_DecryptInit(self.session, m, key)
1✔
1547
        if rv != CKR_OK:
1✔
UNCOV
1548
            raise PyKCS11Error(rv)
×
1549

1550
    def decryptUpdate(self, data):
1✔
1551
        """
1552
        C_DecryptUpdate
1553

1554
        :param data: the data to be decrypted
1555
        :type data: (binary) string or list/tuple of bytes
1556
        """
1557
        decrypted = ckbytelist()
1✔
1558
        encrypted = ckbytelist(data)
1✔
1559
        rv = self.lib.C_DecryptUpdate(self.session, encrypted, decrypted)
1✔
1560
        if rv != CKR_OK:
1✔
UNCOV
1561
            raise PyKCS11Error(rv)
×
1562
        return decrypted
1✔
1563

1564
    def decryptFinal(self):
1✔
1565
        """
1566
        C_DecryptFinal
1567

1568
        :return: the last part of the decrypted data
1569
        :rtype: (binary) string or list/tuple of bytes
1570
        """
1571
        decrypted = ckbytelist()
1✔
1572
        rv = self.lib.C_DecryptFinal(self.session, decrypted)
1✔
1573
        if rv != CKR_OK:
1✔
UNCOV
1574
            raise PyKCS11Error(rv)
×
1575
        return decrypted
1✔
1576

1577
    def wrapKey(self, wrappingKey, key, mecha=MechanismRSAPKCS1):
1✔
1578
        """
1579
        C_WrapKey
1580

1581
        :param wrappingKey: a wrapping key handle
1582
        :type wrappingKey: PyKCS11.LowLevel.CK_OBJECT_HANDLE
1583
        :param key: a handle of the key to be wrapped
1584
        :type key: PyKCS11.LowLevel.CK_OBJECT_HANDLE
1585
        :param mecha: the encrypt mechanism to be used
1586
          (use `MechanismRSAPKCS1` for `CKM_RSA_PKCS`)
1587
        :type mecha: :class:`Mechanism`
1588
        :return: the wrapped key bytes
1589
        :rtype: ckbytelist
1590

1591
        :note: the returned value is an instance of :class:`ckbytelist`.
1592
          You can easily convert it to a binary string with:
1593
          ``bytes(ckbytelistData)``
1594
          or, for Python 2:
1595
          ``''.join(chr(i) for i in ckbytelistData)``
1596

1597
        """
1598
        wrapped = ckbytelist()
1✔
1599
        native = mecha.to_native()
1✔
1600
        # first call get wrapped size
1601
        rv = self.lib.C_WrapKey(self.session, native, wrappingKey, key, wrapped)
1✔
1602
        if rv != CKR_OK:
1✔
UNCOV
1603
            raise PyKCS11Error(rv)
×
1604
        # second call get actual wrapped key data
1605
        rv = self.lib.C_WrapKey(self.session, native, wrappingKey, key, wrapped)
1✔
1606
        if rv != CKR_OK:
1✔
UNCOV
1607
            raise PyKCS11Error(rv)
×
1608
        return wrapped
1✔
1609

1610
    def unwrapKey(self, unwrappingKey, wrappedKey, template, mecha=MechanismRSAPKCS1):
1✔
1611
        """
1612
        C_UnwrapKey
1613

1614
        :param unwrappingKey: the unwrapping key handle
1615
        :type unwrappingKey: PyKCS11.LowLevel.CK_OBJECT_HANDLE
1616
        :param wrappedKey: the bytes of the wrapped key
1617
        :type wrappedKey:  (binary) string or list/tuple of bytes
1618
        :param template: template for the unwrapped key
1619
        :param mecha: the decrypt mechanism to be used (use
1620
          `MechanismRSAPKCS1` for `CKM_RSA_PKCS`)
1621
        :type mecha: :class:`Mechanism`
1622
        :return: the unwrapped key object
1623
        :rtype: PyKCS11.LowLevel.CK_OBJECT_HANDLE
1624

1625
        """
1626
        m = mecha.to_native()
1✔
1627
        data1 = ckbytelist(wrappedKey)
1✔
1628
        handle = PyKCS11.LowLevel.CK_OBJECT_HANDLE()
1✔
1629
        attrs = self._template2ckattrlist(template)
1✔
1630
        rv = self.lib.C_UnwrapKey(self.session, m, unwrappingKey, data1, attrs, handle)
1✔
1631
        if rv != CKR_OK:
1✔
UNCOV
1632
            raise PyKCS11Error(rv)
×
1633
        return handle
1✔
1634

1635
    def deriveKey(self, baseKey, template, mecha):
1✔
1636
        """
1637
        C_DeriveKey
1638

1639
        :param baseKey: the base key handle
1640
        :type baseKey: PyKCS11.LowLevel.CK_OBJECT_HANDLE
1641
        :param template: template for the unwrapped key
1642
        :param mecha: the decrypt mechanism to be used (use
1643
          `ECDH1_DERIVE_Mechanism(...)` for `CKM_ECDH1_DERIVE`)
1644
        :type mecha: :class:`Mechanism`
1645
        :return: the unwrapped key object
1646
        :rtype: PyKCS11.LowLevel.CK_OBJECT_HANDLE
1647
        """
1648
        m = mecha.to_native()
1✔
1649
        handle = PyKCS11.LowLevel.CK_OBJECT_HANDLE()
1✔
1650
        attrs = self._template2ckattrlist(template)
1✔
1651
        rv = self.lib.C_DeriveKey(self.session, m, baseKey, attrs, handle)
1✔
1652
        if rv != CKR_OK:
1✔
UNCOV
1653
            raise PyKCS11Error(rv)
×
1654
        return handle
1✔
1655

1656
    def isNum(self, p11_type):
1✔
1657
        """
1658
        is the type a numerical value?
1659

1660
        :param p11_type: PKCS#11 type like `CKA_CERTIFICATE_TYPE`
1661
        :rtype: bool
1662
        """
1663
        if p11_type in (
1✔
1664
            CKA_CERTIFICATE_TYPE,
1665
            CKA_CLASS,
1666
            CKA_HW_FEATURE_TYPE,
1667
            CKA_KEY_GEN_MECHANISM,
1668
            CKA_KEY_TYPE,
1669
            CKA_MODULUS_BITS,
1670
            CKA_VALUE_BITS,
1671
            CKA_VALUE_LEN,
1672
        ):
1673
            return True
1✔
1674
        return False
1✔
1675

1676
    def isString(self, p11_type):
1✔
1677
        """
1678
        is the type a string value?
1679

1680
        :param p11_type: PKCS#11 type like `CKA_LABEL`
1681
        :rtype: bool
1682
        """
1683
        if p11_type in (CKA_LABEL, CKA_APPLICATION):
1✔
1684
            return True
1✔
1685
        return False
1✔
1686

1687
    def isBool(self, p11_type):
1✔
1688
        """
1689
        is the type a boolean value?
1690

1691
        :param p11_type: PKCS#11 type like `CKA_ALWAYS_SENSITIVE`
1692
        :rtype: bool
1693
        """
1694
        if p11_type in (
1✔
1695
            CKA_ALWAYS_AUTHENTICATE,
1696
            CKA_ALWAYS_SENSITIVE,
1697
            CKA_DECRYPT,
1698
            CKA_DERIVE,
1699
            CKA_ENCRYPT,
1700
            CKA_EXTRACTABLE,
1701
            CKA_HAS_RESET,
1702
            CKA_LOCAL,
1703
            CKA_MODIFIABLE,
1704
            CKA_COPYABLE,
1705
            CKA_DESTROYABLE,
1706
            CKA_NEVER_EXTRACTABLE,
1707
            CKA_PRIVATE,
1708
            CKA_RESET_ON_INIT,
1709
            CKA_SECONDARY_AUTH,
1710
            CKA_SENSITIVE,
1711
            CKA_SIGN,
1712
            CKA_SIGN_RECOVER,
1713
            CKA_TOKEN,
1714
            CKA_TRUSTED,
1715
            CKA_UNWRAP,
1716
            CKA_VERIFY,
1717
            CKA_VERIFY_RECOVER,
1718
            CKA_WRAP,
1719
            CKA_WRAP_WITH_TRUSTED,
1720
        ):
1721
            return True
1✔
1722
        return False
1✔
1723

1724
    def isBin(self, p11_type):
1✔
1725
        """
1726
        is the type a byte array value?
1727

1728
        :param p11_type: PKCS#11 type like `CKA_MODULUS`
1729
        :rtype: bool
1730
        """
1731
        return (
1✔
1732
            (not self.isBool(p11_type))
1733
            and (not self.isString(p11_type))
1734
            and (not self.isNum(p11_type))
1735
        )
1736

1737
    def isAttributeList(self, p11_type):
1✔
1738
        """
1739
        is the type a attribute list value?
1740

1741
        :param p11_type: PKCS#11 type like `CKA_WRAP_TEMPLATE`
1742
        :rtype: bool
1743
        """
1744
        if p11_type in (CKA_WRAP_TEMPLATE, CKA_UNWRAP_TEMPLATE):
1✔
1745
            return True
1✔
1746
        return False
1✔
1747

1748
    def _template2ckattrlist(self, template):
1✔
1749
        t = PyKCS11.LowLevel.ckattrlist(len(template))
1✔
1750
        for x, attr in enumerate(template):
1✔
1751
            if self.isNum(attr[0]):
1✔
1752
                t[x].SetNum(attr[0], int(attr[1]))
1✔
1753
            elif self.isString(attr[0]):
1✔
1754
                t[x].SetString(attr[0], str(attr[1]))
1✔
1755
            elif self.isBool(attr[0]):
1✔
1756
                t[x].SetBool(attr[0], attr[1] == CK_TRUE)
1✔
1757
            elif self.isAttributeList(attr[0]):
1✔
1758
                t[x].SetList(attr[0], self._template2ckattrlist(attr[1]))
1✔
1759
            elif self.isBin(attr[0]):
1✔
1760
                attrBin = attr[1]
1✔
1761
                attrStr = attr[1]
1✔
1762
                if isinstance(attr[1], int):
1✔
UNCOV
1763
                    attrStr = str(attr[1])
×
1764
                if isinstance(attr[1], bytes):
1✔
1765
                    attrBin = ckbytelist(attrStr)
1✔
1766
                t[x].SetBin(attr[0], attrBin)
1✔
1767
            else:
UNCOV
1768
                raise PyKCS11Error(-2, f"attr: {attr[0]:08X}")
×
1769
        return t
1✔
1770

1771
    def generateKey(self, template, mecha=MechanismAESGENERATEKEY):
1✔
1772
        """
1773
        generate a secret key
1774

1775
        :param template: template for the secret key
1776
        :param mecha: mechanism to use
1777
        :return: handle of the generated key
1778
        :rtype: PyKCS11.LowLevel.CK_OBJECT_HANDLE
1779
        """
1780
        t = self._template2ckattrlist(template)
1✔
1781
        ck_handle = PyKCS11.LowLevel.CK_OBJECT_HANDLE()
1✔
1782
        m = mecha.to_native()
1✔
1783
        rv = self.lib.C_GenerateKey(self.session, m, t, ck_handle)
1✔
1784
        if rv != CKR_OK:
1✔
UNCOV
1785
            raise PyKCS11Error(rv)
×
1786
        return ck_handle
1✔
1787

1788
    def generateKeyPair(
1✔
1789
        self, templatePub, templatePriv, mecha=MechanismRSAGENERATEKEYPAIR
1790
    ):
1791
        """
1792
        generate a key pair
1793

1794
        :param templatePub: template for the public key
1795
        :param templatePriv:  template for the private key
1796
        :param mecha: mechanism to use
1797
        :return: a tuple of handles (pub, priv)
1798
        :rtype: tuple
1799
        """
1800
        tPub = self._template2ckattrlist(templatePub)
1✔
1801
        tPriv = self._template2ckattrlist(templatePriv)
1✔
1802
        ck_pub_handle = PyKCS11.LowLevel.CK_OBJECT_HANDLE()
1✔
1803
        ck_prv_handle = PyKCS11.LowLevel.CK_OBJECT_HANDLE()
1✔
1804
        m = mecha.to_native()
1✔
1805
        rv = self.lib.C_GenerateKeyPair(
1✔
1806
            self.session, m, tPub, tPriv, ck_pub_handle, ck_prv_handle
1807
        )
1808

1809
        if rv != CKR_OK:
1✔
1810
            raise PyKCS11Error(rv)
1✔
1811
        return ck_pub_handle, ck_prv_handle
1✔
1812

1813
    def findObjects(self, template=()):
1✔
1814
        """
1815
        find the objects matching the template pattern
1816

1817
        :param template: list of attributes tuples (attribute,value).
1818
          The default value is () and all the objects are returned
1819
        :type template: list
1820
        :return: a list of object ids
1821
        :rtype: list
1822
        """
1823
        t = self._template2ckattrlist(template)
1✔
1824

1825
        # we search for 10 objects by default. speed/memory tradeoff
1826
        result = PyKCS11.LowLevel.ckulonglist(10)
1✔
1827

1828
        rv = self.lib.C_FindObjectsInit(self.session, t)
1✔
1829
        if rv != CKR_OK:
1✔
UNCOV
1830
            raise PyKCS11Error(rv)
×
1831

1832
        res = []
1✔
1833
        while True:
1✔
1834
            rv = self.lib.C_FindObjects(self.session, result)
1✔
1835
            if rv != CKR_OK:
1✔
UNCOV
1836
                raise PyKCS11Error(rv)
×
1837
            for x in result:
1✔
1838
                # make a copy of the handle: the original value get
1839
                # corrupted (!!)
1840
                a = CK_OBJECT_HANDLE(self)
1✔
1841
                a.assign(x)
1✔
1842
                res.append(a)
1✔
1843
            if len(result) == 0:
1✔
1844
                break
1✔
1845

1846
        rv = self.lib.C_FindObjectsFinal(self.session)
1✔
1847
        if rv != CKR_OK:
1✔
UNCOV
1848
            raise PyKCS11Error(rv)
×
1849
        return res
1✔
1850

1851
    def getAttributeValue(self, obj_id, attr, allAsBinary=False):
1✔
1852
        """
1853
        C_GetAttributeValue
1854

1855
        :param obj_id: object ID returned by :func:`findObjects`
1856
        :type obj_id: PyKCS11.LowLevel.CK_OBJECT_HANDLE
1857
        :param attr: list of attributes
1858
        :type attr: list
1859
        :param allAsBinary: return all values as binary data; default is False.
1860
        :type allAsBinary: Boolean
1861
        :return: a list of values corresponding to the list of attributes
1862
        :rtype: list
1863

1864
        :see: :func:`getAttributeValue_fragmented`
1865

1866
        :note: if allAsBinary is True the function do not convert results to
1867
          Python types (i.e.: CKA_TOKEN to Bool, CKA_CLASS to int, ...).
1868

1869
          Binary data is returned as :class:`ckbytelist` type, usable
1870
          as a list containing only bytes.
1871
          You can easly convert it to a binary string with:
1872
          ``bytes(ckbytelistVariable)``
1873
          or, for Python 2:
1874
          ``''.join(chr(i) for i in ckbytelistVariable)``
1875

1876
        """
1877
        valTemplate = PyKCS11.LowLevel.ckattrlist(len(attr))
1✔
1878
        for index, value in enumerate(attr):
1✔
1879
            valTemplate[index].SetType(value)
1✔
1880
        # first call to get the attribute size and reserve the memory
1881
        rv = self.lib.C_GetAttributeValue(self.session, obj_id, valTemplate)
1✔
1882
        if rv in (
1✔
1883
            CKR_ATTRIBUTE_TYPE_INVALID,
1884
            CKR_ATTRIBUTE_SENSITIVE,
1885
            CKR_ARGUMENTS_BAD,
1886
        ):
1887
            return self.getAttributeValue_fragmented(obj_id, attr, allAsBinary)
1✔
1888

1889
        if rv != CKR_OK:
1✔
UNCOV
1890
            raise PyKCS11Error(rv)
×
1891
        # second call to get the attribute value
1892
        rv = self.lib.C_GetAttributeValue(self.session, obj_id, valTemplate)
1✔
1893
        if rv != CKR_OK:
1✔
UNCOV
1894
            raise PyKCS11Error(rv)
×
1895

1896
        res = []
1✔
1897
        for x in range(len(attr)):
1✔
1898
            if allAsBinary:
1✔
1899
                res.append(valTemplate[x].GetBin())
1✔
1900
            elif valTemplate[x].IsNum():
1✔
1901
                res.append(valTemplate[x].GetNum())
1✔
1902
            elif valTemplate[x].IsBool():
1✔
1903
                res.append(valTemplate[x].GetBool())
1✔
1904
            elif valTemplate[x].IsString():
1✔
1905
                res.append(valTemplate[x].GetString())
1✔
1906
            elif valTemplate[x].IsBin():
1✔
1907
                res.append(valTemplate[x].GetBin())
1✔
1908
            else:
1909
                raise PyKCS11Error(-2, f"valTemplate: {valTemplate[x]:08X}")
×
1910

1911
        return res
1✔
1912

1913
    def getAttributeValue_fragmented(self, obj_id, attr, allAsBinary=False):
1✔
1914
        """
1915
        Same as :func:`getAttributeValue` except that when some attribute
1916
        is sensitive or unknown an empty value (None) is returned.
1917

1918
        Note: this is achived by getting attributes one by one.
1919

1920
        :see: :func:`getAttributeValue`
1921
        """
1922
        # some attributes does not exists or is sensitive
1923
        # but we don't know which ones. So try one by one
1924
        valTemplate = PyKCS11.LowLevel.ckattrlist(1)
1✔
1925
        res = []
1✔
1926
        for elt in attr:
1✔
1927
            valTemplate[0].Reset()
1✔
1928
            valTemplate[0].SetType(elt)
1✔
1929
            # first call to get the attribute size and reserve the memory
1930
            rv = self.lib.C_GetAttributeValue(self.session, obj_id, valTemplate)
1✔
1931
            if rv in (
1✔
1932
                CKR_ATTRIBUTE_TYPE_INVALID,
1933
                CKR_ATTRIBUTE_SENSITIVE,
1934
                CKR_ARGUMENTS_BAD,
1935
            ):
1936
                # append an empty value
1937
                res.append(None)
1✔
1938
                continue
1✔
1939

1940
            if rv != CKR_OK:
1✔
UNCOV
1941
                raise PyKCS11Error(rv)
×
1942
            # second call to get the attribute value
1943
            rv = self.lib.C_GetAttributeValue(self.session, obj_id, valTemplate)
1✔
1944
            if rv != CKR_OK:
1✔
UNCOV
1945
                raise PyKCS11Error(rv)
×
1946

1947
            if allAsBinary:
1✔
1948
                res.append(valTemplate[0].GetBin())
×
1949
            elif valTemplate[0].IsNum():
1✔
1950
                res.append(valTemplate[0].GetNum())
1✔
1951
            elif valTemplate[0].IsBool():
1✔
1952
                res.append(valTemplate[0].GetBool())
1✔
1953
            elif valTemplate[0].IsString():
1✔
1954
                res.append(valTemplate[0].GetString())
1✔
1955
            elif valTemplate[0].IsBin():
1✔
1956
                res.append(valTemplate[0].GetBin())
1✔
1957
            elif valTemplate[0].IsAttributeList():
1✔
1958
                res.append(valTemplate[0].GetBin())
1✔
1959
            else:
1960
                raise PyKCS11Error(-2)
×
1961

1962
        return res
1✔
1963

1964
    def setAttributeValue(self, obj_id, template):
1✔
1965
        """
1966
        C_SetAttributeValue
1967

1968
        :param obj_id: object ID returned by :func:`findObjects`
1969
        :type obj_id: PyKCS11.LowLevel.CK_OBJECT_HANDLE
1970
        :param template: list of (attribute, value) pairs
1971
        :type template: list
1972
        :return: Nothing
1973
        :rtype: None
1974
        """
1975

1976
        templ = self._template2ckattrlist(template)
1✔
1977
        rv = self.lib.C_SetAttributeValue(self.session, obj_id, templ)
1✔
1978

1979
        if rv != CKR_OK:
1✔
UNCOV
1980
            raise PyKCS11Error(rv)
×
1981

1982
    def seedRandom(self, seed):
1✔
1983
        """
1984
        C_SeedRandom
1985

1986
        :param seed: seed material
1987
        :type seed: iterable
1988
        """
1989
        low_seed = ckbytelist(seed)
1✔
1990
        rv = self.lib.C_SeedRandom(self.session, low_seed)
1✔
1991
        if rv != CKR_OK:
1✔
UNCOV
1992
            raise PyKCS11Error(rv)
×
1993

1994
    def generateRandom(self, size=16):
1✔
1995
        """
1996
        C_GenerateRandom
1997

1998
        :param size: number of random bytes to get
1999
        :type size: integer
2000

2001
        :note: the returned value is an instance of :class:`ckbytelist`.
2002
          You can easly convert it to a binary string with:
2003
          ``bytes(random)``
2004
          or, for Python 2:
2005
          ``''.join(chr(i) for i in random)``
2006
        """
2007
        low_rand = ckbytelist([0] * size)
1✔
2008
        rv = self.lib.C_GenerateRandom(self.session, low_rand)
1✔
2009
        if rv != CKR_OK:
1✔
UNCOV
2010
            raise PyKCS11Error(rv)
×
2011
        return low_rand
1✔
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc