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

LudovicRousseau / PyKCS11 / 17499392701

04 Sep 2025 07:40PM UTC coverage: 93.527% (-0.001%) from 93.528%
17499392701

push

github

LudovicRousseau
Makefile: update definition of COVERAGE

dev-requirements.txt lists coverage so the module should be installed.

5635 of 6025 relevant lines covered (93.53%)

0.94 hits per line

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

86.39
/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 threading
1✔
24

25
import PyKCS11.LowLevel
1✔
26

27
from .constants import *
1✔
28

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

35

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

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

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

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

63

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

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

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

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

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

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

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

110

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

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

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

123
    flags = 0
1✔
124

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

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

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

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

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

177

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

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

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

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

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

215

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

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

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

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

247

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

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

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

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

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

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

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

290

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

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

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

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

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

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

397

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

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

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

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

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

434

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

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

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

456

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

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

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

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

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

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

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

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

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

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

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

526
        return self
1✔
527

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

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

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

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

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

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

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

564
        self.pkcs11dll_filename = None
1✔
565

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

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

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

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

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

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

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

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

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

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

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

639
        return s
1✔
640

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

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

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

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

697
        return t
1✔
698

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

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

715
        return Session(self, se)
1✔
716

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

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

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

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

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

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

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

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

773
        return i
1✔
774

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

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

789
        return slot
×
790

791

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

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

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

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

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

819

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

826

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

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

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

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

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

848
        self._param.ulTagBits = tagBits
1✔
849

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

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

859

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

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

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

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

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

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

885

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

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

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

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

918

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

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

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

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

947

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

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

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

961
        self._param.kdf = kdf
1✔
962

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

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

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

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

984

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

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

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

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

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

1005

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

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

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

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

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

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

1033

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

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

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

1045

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

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

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

1057

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

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

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

1069

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

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

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

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

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

1093

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

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

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

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

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

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

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

1125

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

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

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

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

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

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

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

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

1180

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

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

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

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

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

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

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

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

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

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

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

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

1258
        del self
1✔
1259

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

1863
        :see: :func:`getAttributeValue_fragmented`
1864

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

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

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

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

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

1910
        return res
1✔
1911

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

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

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

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

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

1961
        return res
1✔
1962

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

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

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

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

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

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

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

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

2000
        :note: the returned value is an instance of :class:`ckbytelist`.
2001
          You can easly convert it to a binary string with:
2002
          ``bytes(random)``
2003
          or, for Python 2:
2004
          ``''.join(chr(i) for i in random)``
2005
        """
2006
        low_rand = ckbytelist([0] * size)
1✔
2007
        rv = self.lib.C_GenerateRandom(self.session, low_rand)
1✔
2008
        if rv != CKR_OK:
1✔
2009
            raise PyKCS11Error(rv)
×
2010
        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