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

LudovicRousseau / PyKCS11 / 12965932525

25 Jan 2025 02:47PM UTC coverage: 86.578% (-0.1%) from 86.678%
12965932525

push

github

LudovicRousseau
bugfix: store CKM_CONCATENATE_BASE_AND_KEY parameter in mechanism context

It's needed to be sure that encKey will not be destroyed by the garbage
collector while the mechanism object is still in use. I guess this is
necessary because the assignment:

self._mech.pParameter = self._encKey

internally copies only a pointer to self._encKey, which would get
invalidated if one destroys self._encKey and that's why it should be
attached to self. It wasn't obvious for me either, but this is how it
works (I guess). The other mechanisms do pretty much the same thing --
i.e. they store the parameter in self._param usually.

Note:
.pParameter is just a property of a CK_MECHANISM() object.

0 of 7 new or added lines in 2 files covered. (0.0%)

1 existing line in 1 file now uncovered.

2993 of 3457 relevant lines covered (86.58%)

0.87 hits per line

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

80.5
/PyKCS11/__init__.py
1
#   Copyright (C) 2006-2015 Ludovic Rousseau (ludovic.rousseau@free.fr)
2
#   Copyright (C) 2010 Giuseppe Amato (additions to original interface)
3
#
4
# This file is free software; you can redistribute it and/or modify it
5
# under the terms of the GNU General Public License as published by
6
# the Free Software Foundation; either version 2 of the License, or
7
# (at your option) any later version.
8
#
9
# This program is distributed in the hope that it will be useful, but
10
# WITHOUT ANY WARRANTY; without even the implied warranty of
11
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12
# General Public License for more details.
13
#
14
# You should have received a copy of the GNU General Public License
15
# along with this program; if not, write to the Free Software
16
# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
17

18

19
import os
1✔
20
import sys
1✔
21

22
import PyKCS11.LowLevel
1✔
23

24
# redefine PKCS#11 constants
25
CK_TRUE = PyKCS11.LowLevel.CK_TRUE
1✔
26
CK_FALSE = PyKCS11.LowLevel.CK_FALSE
1✔
27
CK_UNAVAILABLE_INFORMATION = PyKCS11.LowLevel.CK_UNAVAILABLE_INFORMATION
1✔
28
CK_EFFECTIVELY_INFINITE = PyKCS11.LowLevel.CK_EFFECTIVELY_INFINITE
1✔
29
CK_INVALID_HANDLE = PyKCS11.LowLevel.CK_INVALID_HANDLE
1✔
30

31
CKA = {}
1✔
32
CKC = {}
1✔
33
CKD = {}
1✔
34
CKF = {}
1✔
35
CKG = {}
1✔
36
CKH = {}
1✔
37
CKK = {}
1✔
38
CKM = {}
1✔
39
CKO = {}
1✔
40
CKR = {}
1✔
41
CKS = {}
1✔
42
CKU = {}
1✔
43
CKZ = {}
1✔
44

45
# redefine PKCS#11 constants using well known prefixes
46
for x in PyKCS11.LowLevel.__dict__.keys():
1✔
47
    if (
1✔
48
        x[:4] == "CKA_"
49
        or x[:4] == "CKC_"
50
        or x[:4] == "CKD_"
51
        or x[:4] == "CKF_"
52
        or x[:4] == "CKG_"
53
        or x[:4] == "CKH_"
54
        or x[:4] == "CKK_"
55
        or x[:4] == "CKM_"
56
        or x[:4] == "CKO_"
57
        or x[:4] == "CKR_"
58
        or x[:4] == "CKS_"
59
        or x[:4] == "CKU_"
60
        or x[:4] == "CKZ_"
61
    ):
62
        a = f"{x}=PyKCS11.LowLevel.{x}"
1✔
63
        exec(a)
1✔
64
        if x[3:] != "_VENDOR_DEFINED":
1✔
65
            eval(x[:3])[eval(x)] = x  # => CKM[CKM_RSA_PKCS] = 'CKM_RSA_PKCS'
1✔
66
            eval(x[:3])[x] = eval(x)  # => CKM['CKM_RSA_PKCS'] = CKM_RSA_PKCS
1✔
67

68
# special CKR[] values
69
CKR[-4] = "C_GetFunctionList() not found"
1✔
70
CKR[-3] = "Unknown format"
1✔
71
CKR[-2] = "Unkown PKCS#11 type"
1✔
72
CKR[-1] = "Load"
1✔
73

74

75
class ckbytelist(PyKCS11.LowLevel.ckbytelist):
1✔
76
    """
77
    add a __repr__() method to the LowLevel equivalent
78
    """
79

80
    def __init__(self, data=None):
1✔
81
        if data is None:
1✔
82
            data = 0
1✔
83
        elif isinstance(data, str):
1✔
84
            data = data.encode("utf-8")
1✔
85
        elif isinstance(data, (bytes, list, ckbytelist)):
1✔
86
            data = bytes(data)
1✔
87
        else:
88
            raise PyKCS11.PyKCS11Error(-3, text=str(type(data)))
1✔
89
        super().__init__(data)
1✔
90

91
    def __repr__(self):
1✔
92
        """
93
        return the representation of a tuple
94
        the __str__ method will use it also
95
        """
96
        rep = [int(elt) for elt in self]
1✔
97
        return repr(rep)
1✔
98

99

100
class CK_OBJECT_HANDLE(PyKCS11.LowLevel.CK_OBJECT_HANDLE):
1✔
101
    """
102
    add a __repr__() method to the LowLevel equivalent
103
    """
104

105
    def __init__(self, session):
1✔
106
        PyKCS11.LowLevel.CK_OBJECT_HANDLE.__init__(self)
1✔
107
        self.session = session
1✔
108
        pass
1✔
109

110
    def to_dict(self):
1✔
111
        """
112
        convert the fields of the object into a dictionnary
113
        """
114
        # all the attibutes defined by PKCS#11
115
        all_attributes = PyKCS11.CKA.keys()
1✔
116

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

120
        # all the attributes of the object
121
        attributes = self.session.getAttributeValue(self, all_attributes)
1✔
122

123
        dico = dict()
1✔
124
        for key, attr in zip(all_attributes, attributes):
1✔
125
            if attr is None:
1✔
126
                continue
1✔
127
            if key == CKA_CLASS:
1✔
128
                dico[PyKCS11.CKA[key]] = PyKCS11.CKO[attr]
1✔
129
            elif key == CKA_CERTIFICATE_TYPE:
1✔
130
                dico[PyKCS11.CKA[key]] = PyKCS11.CKC[attr]
×
131
            elif key == CKA_KEY_TYPE:
1✔
132
                dico[PyKCS11.CKA[key]] = PyKCS11.CKK[attr]
1✔
133
            else:
134
                dico[PyKCS11.CKA[key]] = attr
1✔
135
        return dico
1✔
136

137
    def __repr__(self):
1✔
138
        """
139
        text representation of the object
140
        """
141
        dico = self.to_dict()
1✔
142
        lines = list()
1✔
143
        for key in sorted(dico.keys()):
1✔
144
            lines.append(f"{key}: {dico[key]}")
1✔
145
        return "\n".join(lines)
1✔
146

147

148
class CkClass:
1✔
149
    """
150
    Base class for CK_* classes
151
    """
152

153
    # dictionnary of integer_value: text_value for the flags bits
154
    flags_dict = dict()
1✔
155

156
    # dictionnary of fields names and types
157
    # type can be "pair", "flags" or "text"
158
    fields = dict()
1✔
159

160
    flags = 0
1✔
161

162
    def flags2text(self):
1✔
163
        """
164
        parse the `self.flags` field and create a list of `CKF_*` strings
165
        corresponding to bits set in flags
166

167
        :return: a list of strings
168
        :rtype: list
169
        """
170
        r = []
1✔
171
        for v in self.flags_dict.keys():
1✔
172
            if self.flags & v:
1✔
173
                r.append(self.flags_dict[v])
1✔
174
        return r
1✔
175

176
    def to_dict(self):
1✔
177
        """
178
        convert the fields of the object into a dictionnary
179
        """
180
        dico = dict()
1✔
181
        for field in self.fields.keys():
1✔
182
            if field == "flags":
1✔
183
                dico[field] = self.flags2text()
1✔
184
            elif field == "state":
1✔
185
                dico[field] = self.state2text()
1✔
186
            else:
187
                dico[field] = eval("self." + field)
1✔
188
        return dico
1✔
189

190
    def __str__(self):
1✔
191
        """
192
        text representation of the object
193
        """
194
        dico = self.to_dict()
1✔
195
        lines = list()
1✔
196
        for key in sorted(dico.keys()):
1✔
197
            type = self.fields[key]
1✔
198
            if type == "flags":
1✔
199
                lines.append("{}: {}".format(key, ", ".join(dico[key])))
1✔
200
            elif type == "pair":
1✔
201
                lines.append("%s: " % key + "%d.%d" % dico[key])
1✔
202
            else:
203
                lines.append(f"{key}: {dico[key]}")
1✔
204
        return "\n".join(lines)
1✔
205

206

207
class CK_SLOT_INFO(CkClass):
1✔
208
    """
209
    matches the PKCS#11 CK_SLOT_INFO structure
210

211
    :ivar slotDescription: blank padded
212
    :type slotDescription: string
213
    :ivar manufacturerID: blank padded
214
    :type manufacturerID: string
215
    :ivar flags: See :func:`CkClass.flags2text`
216
    :type flags: integer
217
    :ivar hardwareVersion: 2 elements list
218
    :type hardwareVersion: list
219
    :ivar firmwareVersion: 2 elements list
220
    :type firmwareVersion: list
221
    """
222

223
    flags_dict = {
1✔
224
        CKF_TOKEN_PRESENT: "CKF_TOKEN_PRESENT",
225
        CKF_REMOVABLE_DEVICE: "CKF_REMOVABLE_DEVICE",
226
        CKF_HW_SLOT: "CKF_HW_SLOT",
227
    }
228

229
    fields = {
1✔
230
        "slotDescription": "text",
231
        "manufacturerID": "text",
232
        "flags": "flags",
233
        "hardwareVersion": "text",
234
        "firmwareVersion": "text",
235
    }
236

237

238
class CK_INFO(CkClass):
1✔
239
    """
240
    matches the PKCS#11 CK_INFO structure
241

242
    :ivar cryptokiVersion: Cryptoki interface version
243
    :type cryptokiVersion: integer
244
    :ivar manufacturerID: blank padded
245
    :type manufacturerID: string
246
    :ivar flags: must be zero
247
    :type flags: integer
248
    :ivar libraryDescription: blank padded
249
    :type libraryDescription: string
250
    :var libraryVersion: 2 elements list
251
    :type libraryVersion: list
252
    """
253

254
    fields = {
1✔
255
        "cryptokiVersion": "pair",
256
        "manufacturerID": "text",
257
        "flags": "flags",
258
        "libraryDescription": "text",
259
        "libraryVersion": "pair",
260
    }
261

262

263
class CK_SESSION_INFO(CkClass):
1✔
264
    """
265
    matches the PKCS#11 CK_SESSION_INFO structure
266

267
    :ivar slotID: ID of the slot that interfaces with the token
268
    :type slotID: integer
269
    :ivar state: state of the session
270
    :type state: integer
271
    :ivar flags: bit flags that define the type of session
272
    :type flags: integer
273
    :ivar ulDeviceError: an error code defined by the cryptographic token
274
    :type ulDeviceError: integer
275
    """
276

277
    flags_dict = {
1✔
278
        CKF_RW_SESSION: "CKF_RW_SESSION",
279
        CKF_SERIAL_SESSION: "CKF_SERIAL_SESSION",
280
    }
281

282
    def state2text(self):
1✔
283
        """
284
        parse the `self.state` field and return a `CKS_*` string
285
        corresponding to the state
286

287
        :return: a string
288
        :rtype: string
289
        """
290
        return CKS[self.state]
1✔
291

292
    fields = {
1✔
293
        "slotID": "text",
294
        "state": "text",
295
        "flags": "flags",
296
        "ulDeviceError": "text",
297
    }
298

299

300
class CK_TOKEN_INFO(CkClass):
1✔
301
    """
302
    matches the PKCS#11 CK_TOKEN_INFO structure
303

304
    :ivar label: blank padded
305
    :type label: string
306
    :ivar manufacturerID: blank padded
307
    :type manufacturerID: string
308
    :ivar model: string blank padded
309
    :type model: string
310
    :ivar serialNumber: string blank padded
311
    :type serialNumber: string
312
    :ivar flags:
313
    :type flags: integer
314
    :ivar ulMaxSessionCount:
315
    :type ulMaxSessionCount: integer
316
    :ivar ulSessionCount:
317
    :type ulSessionCount: integer
318
    :ivar ulMaxRwSessionCount:
319
    :type ulMaxRwSessionCount: integer
320
    :ivar ulRwSessionCount:
321
    :type ulRwSessionCount: integer
322
    :ivar ulMaxPinLen:
323
    :type ulMaxPinLen: integer
324
    :ivar ulMinPinLen:
325
    :type ulMinPinLen: integer
326
    :ivar ulTotalPublicMemory:
327
    :type ulTotalPublicMemory: integer
328
    :ivar ulFreePublicMemory:
329
    :type ulFreePublicMemory: integer
330
    :ivar ulTotalPrivateMemory:
331
    :type ulTotalPrivateMemory: integer
332
    :ivar ulFreePrivateMemory:
333
    :type ulFreePrivateMemory: integer
334
    :ivar hardwareVersion: 2 elements list
335
    :type hardwareVersion: list
336
    :ivar firmwareVersion: 2 elements list
337
    :type firmwareVersion: list
338
    :ivar utcTime: string
339
    :type utcTime: string
340
    """
341

342
    flags_dict = {
1✔
343
        CKF_RNG: "CKF_RNG",
344
        CKF_WRITE_PROTECTED: "CKF_WRITE_PROTECTED",
345
        CKF_LOGIN_REQUIRED: "CKF_LOGIN_REQUIRED",
346
        CKF_USER_PIN_INITIALIZED: "CKF_USER_PIN_INITIALIZED",
347
        CKF_RESTORE_KEY_NOT_NEEDED: "CKF_RESTORE_KEY_NOT_NEEDED",
348
        CKF_CLOCK_ON_TOKEN: "CKF_CLOCK_ON_TOKEN",
349
        CKF_PROTECTED_AUTHENTICATION_PATH: "CKF_PROTECTED_AUTHENTICATION_PATH",
350
        CKF_DUAL_CRYPTO_OPERATIONS: "CKF_DUAL_CRYPTO_OPERATIONS",
351
        CKF_TOKEN_INITIALIZED: "CKF_TOKEN_INITIALIZED",
352
        CKF_SECONDARY_AUTHENTICATION: "CKF_SECONDARY_AUTHENTICATION",
353
        CKF_USER_PIN_COUNT_LOW: "CKF_USER_PIN_COUNT_LOW",
354
        CKF_USER_PIN_FINAL_TRY: "CKF_USER_PIN_FINAL_TRY",
355
        CKF_USER_PIN_LOCKED: "CKF_USER_PIN_LOCKED",
356
        CKF_USER_PIN_TO_BE_CHANGED: "CKF_USER_PIN_TO_BE_CHANGED",
357
        CKF_SO_PIN_COUNT_LOW: "CKF_SO_PIN_COUNT_LOW",
358
        CKF_SO_PIN_FINAL_TRY: "CKF_SO_PIN_FINAL_TRY",
359
        CKF_SO_PIN_LOCKED: "CKF_SO_PIN_LOCKED",
360
        CKF_SO_PIN_TO_BE_CHANGED: "CKF_SO_PIN_TO_BE_CHANGED",
361
    }
362

363
    fields = {
1✔
364
        "label": "text",
365
        "manufacturerID": "text",
366
        "model": "text",
367
        "serialNumber": "text",
368
        "flags": "flags",
369
        "ulMaxSessionCount": "text",
370
        "ulSessionCount": "text",
371
        "ulMaxRwSessionCount": "text",
372
        "ulRwSessionCount": "text",
373
        "ulMaxPinLen": "text",
374
        "ulMinPinLen": "text",
375
        "ulTotalPublicMemory": "text",
376
        "ulFreePublicMemory": "text",
377
        "ulTotalPrivateMemory": "text",
378
        "ulFreePrivateMemory": "text",
379
        "hardwareVersion": "pair",
380
        "firmwareVersion": "pair",
381
        "utcTime": "text",
382
    }
383

384

385
class CK_MECHANISM_INFO(CkClass):
1✔
386
    """
387
    matches the PKCS#11 CK_MECHANISM_INFO structure
388

389
    :ivar ulMinKeySize: minimum size of the key
390
    :type ulMinKeySize: integer
391
    :ivar ulMaxKeySize: maximum size of the key
392
    :type ulMaxKeySize: integer
393
    :ivar flags: bit flags specifying mechanism capabilities
394
    :type flags: integer
395
    """
396

397
    flags_dict = {
1✔
398
        CKF_HW: "CKF_HW",
399
        CKF_ENCRYPT: "CKF_ENCRYPT",
400
        CKF_DECRYPT: "CKF_DECRYPT",
401
        CKF_DIGEST: "CKF_DIGEST",
402
        CKF_SIGN: "CKF_SIGN",
403
        CKF_SIGN_RECOVER: "CKF_SIGN_RECOVER",
404
        CKF_VERIFY: "CKF_VERIFY",
405
        CKF_VERIFY_RECOVER: "CKF_VERIFY_RECOVER",
406
        CKF_GENERATE: "CKF_GENERATE",
407
        CKF_GENERATE_KEY_PAIR: "CKF_GENERATE_KEY_PAIR",
408
        CKF_WRAP: "CKF_WRAP",
409
        CKF_UNWRAP: "CKF_UNWRAP",
410
        CKF_DERIVE: "CKF_DERIVE",
411
        CKF_EXTENSION: "CKF_EXTENSION",
412
    }
413

414
    fields = {"ulMinKeySize": "text", "ulMaxKeySize": "text", "flags": "flags"}
1✔
415

416

417
class PyKCS11Error(Exception):
1✔
418
    """define the possible PyKCS11 exceptions"""
419

420
    def __init__(self, value, text=""):
1✔
421
        self.value = value
1✔
422
        self.text = text
1✔
423

424
    def __str__(self):
1✔
425
        """
426
        The text representation of a PKCS#11 error is something like:
427
        "CKR_DEVICE_ERROR (0x00000030)"
428
        """
429
        if self.value in CKR:
1✔
430
            if self.value < 0:
1✔
431
                return CKR[self.value] + " (%s)" % self.text
1✔
432
            else:
433
                return CKR[self.value] + " (0x%08X)" % self.value
1✔
434
        elif self.value & CKR_VENDOR_DEFINED:
1✔
435
            return "Vendor error (0x%08X)" % (
1✔
436
                self.value & 0xFFFFFFFF & ~CKR_VENDOR_DEFINED
437
            )
438
        else:
439
            return "Unknown error (0x%08X)" % self.value
1✔
440

441

442
class PyKCS11Lib:
1✔
443
    """high level PKCS#11 binding"""
444

445
    # shared by all instances
446
    _loaded_libs = dict()
1✔
447

448
    def __init__(self):
1✔
449
        self.lib = PyKCS11.LowLevel.CPKCS11Lib()
1✔
450

451
    def __del__(self):
1✔
452
        if (
1✔
453
            PyKCS11
454
            and PyKCS11.__name__
455
            and PyKCS11.LowLevel
456
            and PyKCS11.LowLevel.__name__
457
            and PyKCS11.LowLevel._LowLevel
458
            and PyKCS11.LowLevel._LowLevel.__name__
459
        ):
460

461
            # unload the library
462
            self.unload()
1✔
463

464
    def load(self, pkcs11dll_filename=None, *init_string):
1✔
465
        """
466
        load a PKCS#11 library
467

468
        :type pkcs11dll_filename: string
469
        :param pkcs11dll_filename: the library name.
470
          If this parameter is not set then the environment variable
471
          `PYKCS11LIB` is used instead
472
        :returns: a :class:`PyKCS11Lib` object
473
        :raises: :class:`PyKCS11Error` (-1): when the load fails
474
        """
475
        if pkcs11dll_filename is None:
1✔
476
            pkcs11dll_filename = os.getenv("PYKCS11LIB")
1✔
477
            if pkcs11dll_filename is None:
1✔
478
                raise PyKCS11Error(
×
479
                    -1, "No PKCS11 library specified (set PYKCS11LIB env variable)"
480
                )
481

482
        if hasattr(self, "pkcs11dll_filename"):
1✔
483
            self.unload()  # unload the previous library
1✔
484
            # if the instance was previously initialized,
485
            # create a new low level library object for it
486
            self.lib = PyKCS11.LowLevel.CPKCS11Lib()
1✔
487

488
        # if the lib is already in use: reuse it
489
        if pkcs11dll_filename in PyKCS11Lib._loaded_libs:
1✔
490
            self.lib.Duplicate(PyKCS11Lib._loaded_libs[pkcs11dll_filename]["ref"])
1✔
491
        else:
492
            # else load it
493
            rv = self.lib.Load(pkcs11dll_filename)
1✔
494
            if rv != CKR_OK:
1✔
495
                raise PyKCS11Error(rv, pkcs11dll_filename)
1✔
496
            PyKCS11Lib._loaded_libs[pkcs11dll_filename] = {
1✔
497
                "ref": self.lib,
498
                "nb_users": 0,
499
            }
500

501
        # remember the lib file name
502
        self.pkcs11dll_filename = pkcs11dll_filename
1✔
503

504
        # increase user number
505
        PyKCS11Lib._loaded_libs[pkcs11dll_filename]["nb_users"] += 1
1✔
506

507
        return self
1✔
508

509
    def unload(self):
1✔
510
        """
511
        unload the current instance of a PKCS#11 library
512
        """
513

514
        # in case NO library was found and used
515
        if not hasattr(self, "pkcs11dll_filename"):
1✔
516
            return
1✔
517

518
        if self.pkcs11dll_filename not in PyKCS11Lib._loaded_libs:
1✔
519
            raise PyKCS11Error(
×
520
                PyKCS11.LowLevel.CKR_GENERAL_ERROR, "invalid PyKCS11Lib state"
521
            )
522

523
        # decrease user number
524
        PyKCS11Lib._loaded_libs[self.pkcs11dll_filename]["nb_users"] -= 1
1✔
525

526
        if PyKCS11Lib._loaded_libs[self.pkcs11dll_filename]["nb_users"] == 0:
1✔
527
            # unload only if no more used
528
            self.lib.Unload()
1✔
529

530
        # remove unused entry
531
        # the case < 0 happens if lib loading failed
532
        if PyKCS11Lib._loaded_libs[self.pkcs11dll_filename]["nb_users"] <= 0:
1✔
533
            del PyKCS11Lib._loaded_libs[self.pkcs11dll_filename]
1✔
534

535
        delattr(self, "pkcs11dll_filename")
1✔
536

537
    def initToken(self, slot, pin, label):
1✔
538
        """
539
        C_InitToken
540

541
        :param slot: slot number returned by :func:`getSlotList`
542
        :type slot: integer
543
        :param pin: Security Officer's initial PIN
544
        :param label: new label of the token
545
        """
546
        pin1 = ckbytelist(pin)
1✔
547
        rv = self.lib.C_InitToken(slot, pin1, label)
1✔
548
        if rv != CKR_OK:
1✔
549
            raise PyKCS11Error(rv)
×
550

551
    def getInfo(self):
1✔
552
        """
553
        C_GetInfo
554

555
        :return: a :class:`CK_INFO` object
556
        """
557
        info = PyKCS11.LowLevel.CK_INFO()
1✔
558
        rv = self.lib.C_GetInfo(info)
1✔
559
        if rv != CKR_OK:
1✔
560
            raise PyKCS11Error(rv)
×
561

562
        i = CK_INFO()
1✔
563
        i.cryptokiVersion = (info.cryptokiVersion.major, info.cryptokiVersion.minor)
1✔
564
        i.manufacturerID = info.GetManufacturerID()
1✔
565
        i.flags = info.flags
1✔
566
        i.libraryDescription = info.GetLibraryDescription()
1✔
567
        i.libraryVersion = (info.libraryVersion.major, info.libraryVersion.minor)
1✔
568
        return i
1✔
569

570
    def getSlotList(self, tokenPresent=False):
1✔
571
        """
572
        C_GetSlotList
573

574
        :param tokenPresent: `False` (default) to list all slots,
575
          `True` to list only slots with present tokens
576
        :type tokenPresent: bool
577
        :return: a list of available slots
578
        :rtype: list
579
        """
580
        slotList = PyKCS11.LowLevel.ckintlist()
1✔
581
        rv = self.lib.C_GetSlotList(CK_TRUE if tokenPresent else CK_FALSE, slotList)
1✔
582
        if rv != CKR_OK:
1✔
583
            raise PyKCS11Error(rv)
×
584

585
        s = []
1✔
586
        for x in range(len(slotList)):
1✔
587
            s.append(slotList[x])
1✔
588
        return s
1✔
589

590
    def getSlotInfo(self, slot):
1✔
591
        """
592
        C_GetSlotInfo
593

594
        :param slot: slot number returned by :func:`getSlotList`
595
        :type slot: integer
596
        :return: a :class:`CK_SLOT_INFO` object
597
        """
598
        slotInfo = PyKCS11.LowLevel.CK_SLOT_INFO()
1✔
599
        rv = self.lib.C_GetSlotInfo(slot, slotInfo)
1✔
600
        if rv != CKR_OK:
1✔
601
            raise PyKCS11Error(rv)
×
602

603
        s = CK_SLOT_INFO()
1✔
604
        s.slotDescription = slotInfo.GetSlotDescription()
1✔
605
        s.manufacturerID = slotInfo.GetManufacturerID()
1✔
606
        s.flags = slotInfo.flags
1✔
607
        s.hardwareVersion = slotInfo.GetHardwareVersion()
1✔
608
        s.firmwareVersion = slotInfo.GetFirmwareVersion()
1✔
609

610
        return s
1✔
611

612
    def getTokenInfo(self, slot):
1✔
613
        """
614
        C_GetTokenInfo
615

616
        :param slot: slot number returned by :func:`getSlotList`
617
        :type slot: integer
618
        :return: a :class:`CK_TOKEN_INFO` object
619
        """
620
        tokeninfo = PyKCS11.LowLevel.CK_TOKEN_INFO()
1✔
621
        rv = self.lib.C_GetTokenInfo(slot, tokeninfo)
1✔
622
        if rv != CKR_OK:
1✔
623
            raise PyKCS11Error(rv)
×
624

625
        t = CK_TOKEN_INFO()
1✔
626
        t.label = tokeninfo.GetLabel()
1✔
627
        t.manufacturerID = tokeninfo.GetManufacturerID()
1✔
628
        t.model = tokeninfo.GetModel()
1✔
629
        t.serialNumber = tokeninfo.GetSerialNumber()
1✔
630
        t.flags = tokeninfo.flags
1✔
631
        t.ulMaxSessionCount = tokeninfo.ulMaxSessionCount
1✔
632
        if t.ulMaxSessionCount == CK_UNAVAILABLE_INFORMATION:
1✔
633
            t.ulMaxSessionCount = -1
×
634
        t.ulSessionCount = tokeninfo.ulSessionCount
1✔
635
        if t.ulSessionCount == CK_UNAVAILABLE_INFORMATION:
1✔
636
            t.ulSessionCount = -1
1✔
637
        t.ulMaxRwSessionCount = tokeninfo.ulMaxRwSessionCount
1✔
638
        if t.ulMaxRwSessionCount == CK_UNAVAILABLE_INFORMATION:
1✔
639
            t.ulMaxRwSessionCount = -1
×
640
        t.ulRwSessionCount = tokeninfo.ulRwSessionCount
1✔
641
        if t.ulRwSessionCount == CK_UNAVAILABLE_INFORMATION:
1✔
642
            t.ulRwSessionCount = -1
1✔
643
        t.ulMaxPinLen = tokeninfo.ulMaxPinLen
1✔
644
        t.ulMinPinLen = tokeninfo.ulMinPinLen
1✔
645
        t.ulTotalPublicMemory = tokeninfo.ulTotalPublicMemory
1✔
646

647
        if t.ulTotalPublicMemory == CK_UNAVAILABLE_INFORMATION:
1✔
648
            t.ulTotalPublicMemory = -1
1✔
649
        t.ulFreePublicMemory = tokeninfo.ulFreePublicMemory
1✔
650
        if t.ulFreePublicMemory == CK_UNAVAILABLE_INFORMATION:
1✔
651
            t.ulFreePublicMemory = -1
1✔
652
        t.ulTotalPrivateMemory = tokeninfo.ulTotalPrivateMemory
1✔
653
        if t.ulTotalPrivateMemory == CK_UNAVAILABLE_INFORMATION:
1✔
654
            t.ulTotalPrivateMemory = -1
1✔
655
        t.ulFreePrivateMemory = tokeninfo.ulFreePrivateMemory
1✔
656
        if t.ulFreePrivateMemory == CK_UNAVAILABLE_INFORMATION:
1✔
657
            t.ulFreePrivateMemory = -1
1✔
658
        t.hardwareVersion = (
1✔
659
            tokeninfo.hardwareVersion.major,
660
            tokeninfo.hardwareVersion.minor,
661
        )
662
        t.firmwareVersion = (
1✔
663
            tokeninfo.firmwareVersion.major,
664
            tokeninfo.firmwareVersion.minor,
665
        )
666
        t.utcTime = tokeninfo.GetUtcTime().replace("\000", " ")
1✔
667

668
        return t
1✔
669

670
    def openSession(self, slot, flags=0):
1✔
671
        """
672
        C_OpenSession
673

674
        :param slot: slot number returned by :func:`getSlotList`
675
        :type slot: integer
676
        :param flags: 0 (default), `CKF_RW_SESSION` for RW session
677
        :type flags: integer
678
        :return: a :class:`Session` object
679
        """
680
        se = PyKCS11.LowLevel.CK_SESSION_HANDLE()
1✔
681
        flags |= CKF_SERIAL_SESSION
1✔
682
        rv = self.lib.C_OpenSession(slot, flags, se)
1✔
683
        if rv != CKR_OK:
1✔
684
            raise PyKCS11Error(rv)
×
685

686
        return Session(self, se)
1✔
687

688
    def closeAllSessions(self, slot):
1✔
689
        """
690
        C_CloseAllSessions
691

692
        :param slot: slot number
693
        :type slot: integer
694
        """
695
        rv = self.lib.C_CloseAllSessions(slot)
1✔
696
        if rv != CKR_OK:
1✔
697
            raise PyKCS11Error(rv)
×
698

699
    def getMechanismList(self, slot):
1✔
700
        """
701
        C_GetMechanismList
702

703
        :param slot: slot number returned by :func:`getSlotList`
704
        :type slot: integer
705
        :return: the list of available mechanisms for a slot
706
        :rtype: list
707
        """
708
        mechanismList = PyKCS11.LowLevel.ckintlist()
1✔
709
        rv = self.lib.C_GetMechanismList(slot, mechanismList)
1✔
710
        if rv != CKR_OK:
1✔
711
            raise PyKCS11Error(rv)
×
712

713
        m = []
1✔
714
        for x in range(len(mechanismList)):
1✔
715
            mechanism = mechanismList[x]
1✔
716
            if mechanism >= CKM_VENDOR_DEFINED:
1✔
717
                k = "CKM_VENDOR_DEFINED_0x%X" % (mechanism - CKM_VENDOR_DEFINED)
×
718
                CKM[k] = mechanism
×
719
                CKM[mechanism] = k
×
720
            m.append(CKM[mechanism])
1✔
721
        return m
1✔
722

723
    def getMechanismInfo(self, slot, type):
1✔
724
        """
725
        C_GetMechanismInfo
726

727
        :param slot: slot number returned by :func:`getSlotList`
728
        :type slot: integer
729
        :param type: a `CKM_*` type
730
        :type type: integer
731
        :return: information about a mechanism
732
        :rtype: a :class:`CK_MECHANISM_INFO` object
733
        """
734
        info = PyKCS11.LowLevel.CK_MECHANISM_INFO()
1✔
735
        rv = self.lib.C_GetMechanismInfo(slot, CKM[type], info)
1✔
736
        if rv != CKR_OK:
1✔
737
            raise PyKCS11Error(rv)
×
738

739
        i = CK_MECHANISM_INFO()
1✔
740
        i.ulMinKeySize = info.ulMinKeySize
1✔
741
        i.ulMaxKeySize = info.ulMaxKeySize
1✔
742
        i.flags = info.flags
1✔
743

744
        return i
1✔
745

746
    def waitForSlotEvent(self, flags=0):
1✔
747
        """
748
        C_WaitForSlotEvent
749

750
        :param flags: 0 (default) or `CKF_DONT_BLOCK`
751
        :type flags: integer
752
        :return: slot
753
        :rtype: integer
754
        """
755
        tmp = 0
×
756
        (rv, slot) = self.lib.C_WaitForSlotEvent(flags, tmp)
×
757
        if rv != CKR_OK:
×
758
            raise PyKCS11Error(rv)
×
759

760
        return slot
×
761

762

763
class Mechanism:
1✔
764
    """Wraps CK_MECHANISM"""
765

766
    def __init__(self, mechanism, param=None):
1✔
767
        """
768
        :param mechanism: the mechanism to be used
769
        :type mechanism: integer, any `CKM_*` value
770
        :param param: data to be used as crypto operation parameter
771
          (i.e. the IV for some algorithms)
772
        :type param: string or list/tuple of bytes
773

774
        :see: :func:`Session.decrypt`, :func:`Session.sign`
775
        """
776
        self._mech = PyKCS11.LowLevel.CK_MECHANISM()
1✔
777
        self._mech.mechanism = mechanism
1✔
778
        self._param = None
1✔
779
        if param:
1✔
780
            self._param = ckbytelist(param)
1✔
781
            self._mech.pParameter = self._param
1✔
782
            self._mech.ulParameterLen = len(param)
1✔
783

784
    def to_native(self):
1✔
785
        return self._mech
1✔
786

787

788
MechanismSHA1 = Mechanism(CKM_SHA_1, None)
1✔
789
MechanismRSAPKCS1 = Mechanism(CKM_RSA_PKCS, None)
1✔
790
MechanismRSAGENERATEKEYPAIR = Mechanism(CKM_RSA_PKCS_KEY_PAIR_GEN, None)
1✔
791
MechanismECGENERATEKEYPAIR = Mechanism(CKM_EC_KEY_PAIR_GEN, None)
1✔
792
MechanismAESGENERATEKEY = Mechanism(CKM_AES_KEY_GEN, None)
1✔
793

794

795
class AES_GCM_Mechanism:
1✔
796
    """CKM_AES_GCM warpping mechanism"""
797

798
    def __init__(self, iv, aad, tagBits):
1✔
799
        """
800
        :param iv: initialization vector
801
        :param aad: additional authentication data
802
        :param tagBits: length of authentication tag in bits
803
        """
804
        self._param = PyKCS11.LowLevel.CK_GCM_PARAMS()
1✔
805

806
        self._source_iv = ckbytelist(iv)
1✔
807
        self._param.pIv = self._source_iv
1✔
808
        self._param.ulIvLen = len(self._source_iv)
1✔
809

810
        self._source_aad = ckbytelist(aad)
1✔
811
        self._param.pAAD = self._source_aad
1✔
812
        self._param.ulAADLen = len(self._source_aad)
1✔
813

814
        self._param.ulTagBits = tagBits
1✔
815

816
        self._mech = PyKCS11.LowLevel.CK_MECHANISM()
1✔
817
        self._mech.mechanism = CKM_AES_GCM
1✔
818
        self._mech.pParameter = self._param
1✔
819
        self._mech.ulParameterLen = PyKCS11.LowLevel.CK_GCM_PARAMS_LENGTH
1✔
820

821
    def to_native(self):
1✔
822
        return self._mech
1✔
823

824

825
class AES_CTR_Mechanism:
1✔
826
    """CKM_AES_CTR encryption mechanism"""
827

828
    def __init__(self, counterBits, counterBlock):
1✔
829
        """
830
        :param counterBits: the number of incremented bits in the counter block
831
        :param counterBlock: a 16-byte initial value of the counter block
832
        """
833
        self._param = PyKCS11.LowLevel.CK_AES_CTR_PARAMS()
1✔
834

835
        self._source_cb = ckbytelist(counterBlock)
1✔
836
        self._param.ulCounterBits = counterBits
1✔
837
        self._param.cb = self._source_cb
1✔
838

839
        self._mech = PyKCS11.LowLevel.CK_MECHANISM()
1✔
840
        self._mech.mechanism = CKM_AES_CTR
1✔
841
        self._mech.pParameter = self._param
1✔
842
        self._mech.ulParameterLen = PyKCS11.LowLevel.CK_AES_CTR_PARAMS_LENGTH
1✔
843

844
    def to_native(self):
1✔
845
        return self._mech
1✔
846

847

848
class RSAOAEPMechanism:
1✔
849
    """RSA OAEP Wrapping mechanism"""
850

851
    def __init__(self, hashAlg, mgf, label=None):
1✔
852
        """
853
        :param hashAlg: the hash algorithm to use (like `CKM_SHA256`)
854
        :param mgf: the mask generation function to use (like
855
          `CKG_MGF1_SHA256`)
856
        :param label: the (optional) label to use
857
        """
858
        self._param = PyKCS11.LowLevel.CK_RSA_PKCS_OAEP_PARAMS()
1✔
859
        self._param.hashAlg = hashAlg
1✔
860
        self._param.mgf = mgf
1✔
861
        self._source = None
1✔
862
        self._param.src = CKZ_DATA_SPECIFIED
1✔
863
        if label:
1✔
864
            self._source = ckbytelist(label)
×
865
            self._param.ulSourceDataLen = len(self._source)
×
866
        else:
867
            self._param.ulSourceDataLen = 0
1✔
868
        self._param.pSourceData = self._source
1✔
869
        self._mech = PyKCS11.LowLevel.CK_MECHANISM()
1✔
870
        self._mech.mechanism = CKM_RSA_PKCS_OAEP
1✔
871
        self._mech.pParameter = self._param
1✔
872
        self._mech.ulParameterLen = PyKCS11.LowLevel.CK_RSA_PKCS_OAEP_PARAMS_LENGTH
1✔
873

874
    def to_native(self):
1✔
875
        return self._mech
1✔
876

877

878
class RSA_PSS_Mechanism:
1✔
879
    """RSA PSS Wrapping mechanism"""
880

881
    def __init__(self, mecha, hashAlg, mgf, sLen):
1✔
882
        """
883
        :param mecha: the mechanism to use (like
884
          `CKM_SHA384_RSA_PKCS_PSS`)
885
        :param hashAlg: the hash algorithm to use (like `CKM_SHA384`)
886
        :param mgf: the mask generation function to use (like
887
          `CKG_MGF1_SHA384`)
888
        :param sLen: length, in bytes, of the salt value used in the PSS
889
          encoding (like 0 or the message length)
890
        """
891
        self._param = PyKCS11.LowLevel.CK_RSA_PKCS_PSS_PARAMS()
1✔
892
        self._param.hashAlg = hashAlg
1✔
893
        self._param.mgf = mgf
1✔
894
        self._param.sLen = sLen
1✔
895
        self._mech = PyKCS11.LowLevel.CK_MECHANISM()
1✔
896
        self._mech.mechanism = mecha
1✔
897
        self._mech.pParameter = self._param
1✔
898
        self._mech.ulParameterLen = PyKCS11.LowLevel.CK_RSA_PKCS_PSS_PARAMS_LENGTH
1✔
899

900
    def to_native(self):
1✔
901
        return self._mech
1✔
902

903

904
class ECDH1_DERIVE_Mechanism:
1✔
905
    """CKM_ECDH1_DERIVE key derivation mechanism"""
906

907
    def __init__(self, publicData, kdf=CKD_NULL, sharedData=None):
1✔
908
        """
909
        :param publicData: Other party public key which is EC Point [PC || coord-x || coord-y].
910
        :param kdf: Key derivation function. OPTIONAL. Defaults to CKD_NULL
911
        :param sharedData: additional shared data. OPTIONAL
912
        """
913
        self._param = PyKCS11.LowLevel.CK_ECDH1_DERIVE_PARAMS()
1✔
914

915
        self._param.kdf = kdf
1✔
916

917
        if sharedData:
1✔
918
            self._shared_data = ckbytelist(sharedData)
×
919
            self._param.pSharedData = self._shared_data
×
920
            self._param.ulSharedDataLen = len(self._shared_data)
×
921
        else:
922
            self._source_shared_data = None
1✔
923
            self._param.ulSharedDataLen = 0
1✔
924

925
        self._public_data = ckbytelist(publicData)
1✔
926
        self._param.pPublicData = self._public_data
1✔
927
        self._param.ulPublicDataLen = len(self._public_data)
1✔
928

929
        self._mech = PyKCS11.LowLevel.CK_MECHANISM()
1✔
930
        self._mech.mechanism = CKM_ECDH1_DERIVE
1✔
931
        self._mech.pParameter = self._param
1✔
932
        self._mech.ulParameterLen = PyKCS11.LowLevel.CK_ECDH1_DERIVE_PARAMS_LENGTH
1✔
933

934
    def to_native(self):
1✔
935
        return self._mech
1✔
936

937

938
class CONCATENATE_BASE_AND_KEY_Mechanism:
1✔
939
    """CKM_CONCATENATE_BASE_AND_KEY key derivation mechanism"""
940

941
    def __init__(self, encKey):
1✔
942
        """
943
        :param encKey: a handle of encryption key
944
        """
NEW
945
        self._encKey = encKey
×
946

947
        self._mech = PyKCS11.LowLevel.CK_MECHANISM()
×
948
        self._mech.mechanism = CKM_CONCATENATE_BASE_AND_KEY
×
NEW
949
        self._mech.pParameter = self._encKey
×
950
        self._mech.ulParameterLen = PyKCS11.LowLevel.CK_OBJECT_HANDLE_LENGTH
×
951

952
    def to_native(self):
1✔
953
        return self._mech
×
954

955

956
class KEY_DERIVATION_STRING_DATA_MechanismBase:
1✔
957
    """Base class for mechanisms using derivation string data"""
958

959
    def __init__(self, data, mechType):
1✔
960
        """
961
        :param data: a byte array to concatenate the key with
962
        :param mechType: mechanism type
963
        """
964
        self._param = PyKCS11.LowLevel.CK_KEY_DERIVATION_STRING_DATA()
×
965

966
        self._data = ckbytelist(data)
×
967
        self._param.pData = self._data
×
968
        self._param.ulLen = len(self._data)
×
969

970
        self._mech = PyKCS11.LowLevel.CK_MECHANISM()
×
971
        self._mech.mechanism = mechType
×
972
        self._mech.pParameter = self._param
×
973
        self._mech.ulParameterLen = (
×
974
            PyKCS11.LowLevel.CK_KEY_DERIVATION_STRING_DATA_LENGTH
975
        )
976

977
    def to_native(self):
1✔
978
        return self._mech
×
979

980

981
class CONCATENATE_BASE_AND_DATA_Mechanism(KEY_DERIVATION_STRING_DATA_MechanismBase):
1✔
982
    """CKM_CONCATENATE_BASE_AND_DATA key derivation mechanism"""
983

984
    def __init__(self, data):
1✔
985
        """
986
        :param data: a byte array to concatenate the key with
987
        """
988
        super().__init__(data, CKM_CONCATENATE_BASE_AND_DATA)
×
989

990

991
class CONCATENATE_DATA_AND_BASE_Mechanism(KEY_DERIVATION_STRING_DATA_MechanismBase):
1✔
992
    """CKM_CONCATENATE_DATA_AND_BASE key derivation mechanism"""
993

994
    def __init__(self, data):
1✔
995
        """
996
        :param data: a byte array to concatenate the key with
997
        """
998
        super().__init__(data, CKM_CONCATENATE_DATA_AND_BASE)
×
999

1000

1001
class XOR_BASE_AND_DATA_Mechanism(KEY_DERIVATION_STRING_DATA_MechanismBase):
1✔
1002
    """CKM_XOR_BASE_AND_DATA key derivation mechanism"""
1003

1004
    def __init__(self, data):
1✔
1005
        """
1006
        :param data: a byte array to xor the key with
1007
        """
1008
        super().__init__(data, CKM_XOR_BASE_AND_DATA)
×
1009

1010

1011
class EXTRACT_KEY_FROM_KEY_Mechanism:
1✔
1012
    """CKM_EXTRACT_KEY_FROM_KEY key derivation mechanism"""
1013

1014
    def __init__(self, extractParams):
1✔
1015
        """
1016
        :param extractParams: the index of the first bit of the original key to be used in the newly-derived key.
1017
                              For example if extractParams=5 then the 5 first bits are skipped and not used.
1018
        """
1019
        self._param = PyKCS11.LowLevel.CK_EXTRACT_PARAMS()
×
1020
        self._param.assign(extractParams)
×
1021

1022
        self._mech = PyKCS11.LowLevel.CK_MECHANISM()
×
1023
        self._mech.mechanism = CKM_EXTRACT_KEY_FROM_KEY
×
1024
        self._mech.pParameter = self._param
×
1025
        self._mech.ulParameterLen = PyKCS11.LowLevel.CK_EXTRACT_PARAMS_LENGTH
×
1026

1027
    def to_native(self):
1✔
1028
        return self._mech
×
1029

1030

1031
class DigestSession:
1✔
1032
    def __init__(self, lib, session, mecha):
1✔
1033
        self._lib = lib
1✔
1034
        self._session = session
1✔
1035
        self._mechanism = mecha.to_native()
1✔
1036
        rv = self._lib.C_DigestInit(self._session, self._mechanism)
1✔
1037
        if rv != CKR_OK:
1✔
1038
            raise PyKCS11Error(rv)
×
1039

1040
    def update(self, data):
1✔
1041
        """
1042
        C_DigestUpdate
1043

1044
        :param data: data to add to the digest
1045
        :type data: bytes or string
1046
        """
1047
        data1 = ckbytelist(data)
1✔
1048
        rv = self._lib.C_DigestUpdate(self._session, data1)
1✔
1049
        if rv != CKR_OK:
1✔
1050
            raise PyKCS11Error(rv)
×
1051
        return self
1✔
1052

1053
    def digestKey(self, handle):
1✔
1054
        """
1055
        C_DigestKey
1056

1057
        :param handle: key handle
1058
        :type handle: CK_OBJECT_HANDLE
1059
        """
1060
        rv = self._lib.C_DigestKey(self._session, handle)
×
1061
        if rv != CKR_OK:
×
1062
            raise PyKCS11Error(rv)
×
1063
        return self
×
1064

1065
    def final(self):
1✔
1066
        """
1067
        C_DigestFinal
1068

1069
        :return: the digest
1070
        :rtype: ckbytelist
1071
        """
1072
        digest = ckbytelist()
1✔
1073
        # Get the size of the digest
1074
        rv = self._lib.C_DigestFinal(self._session, digest)
1✔
1075
        if rv != CKR_OK:
1✔
1076
            raise PyKCS11Error(rv)
×
1077
        # Get the actual digest
1078
        rv = self._lib.C_DigestFinal(self._session, digest)
1✔
1079
        if rv != CKR_OK:
1✔
1080
            raise PyKCS11Error(rv)
×
1081
        return digest
1✔
1082

1083

1084
class Session:
1✔
1085
    """Manage :func:`PyKCS11Lib.openSession` objects"""
1086

1087
    def __init__(self, pykcs11, session):
1✔
1088
        """
1089
        :param pykcs11: PyKCS11 library object
1090
        :type pykcs11: PyKCS11Lib
1091
        :param session: session handle
1092
        :type session: instance of :class:`CK_SESSION_HANDLE`
1093
        """
1094
        if not isinstance(pykcs11, PyKCS11Lib):
1✔
1095
            raise TypeError("pykcs11 must be a PyKCS11Lib")
×
1096
        if not isinstance(session, LowLevel.CK_SESSION_HANDLE):
1✔
1097
            raise TypeError("session must be a CK_SESSION_HANDLE")
×
1098

1099
        # hold the PyKCS11Lib reference, so that it's not Garbage Collection'd
1100
        self.pykcs11 = pykcs11
1✔
1101
        self.session = session
1✔
1102

1103
    @property
1✔
1104
    def lib(self):
1✔
1105
        """
1106
        Get the low level lib of the owning PyKCS11Lib
1107
        """
1108
        return self.pykcs11.lib
1✔
1109

1110
    def closeSession(self):
1✔
1111
        """
1112
        C_CloseSession
1113
        """
1114
        rv = self.lib.C_CloseSession(self.session)
1✔
1115
        if rv != CKR_OK:
1✔
1116
            raise PyKCS11Error(rv)
×
1117

1118
    def getSessionInfo(self):
1✔
1119
        """
1120
        C_GetSessionInfo
1121

1122
        :return: a :class:`CK_SESSION_INFO` object
1123
        """
1124
        sessioninfo = PyKCS11.LowLevel.CK_SESSION_INFO()
1✔
1125
        rv = self.lib.C_GetSessionInfo(self.session, sessioninfo)
1✔
1126
        if rv != CKR_OK:
1✔
1127
            raise PyKCS11Error(rv)
×
1128

1129
        s = CK_SESSION_INFO()
1✔
1130
        s.slotID = sessioninfo.slotID
1✔
1131
        s.state = sessioninfo.state
1✔
1132
        s.flags = sessioninfo.flags
1✔
1133
        s.ulDeviceError = sessioninfo.ulDeviceError
1✔
1134
        return s
1✔
1135

1136
    def login(self, pin, user_type=CKU_USER):
1✔
1137
        """
1138
        C_Login
1139

1140
        :param pin: the user's PIN or None for CKF_PROTECTED_AUTHENTICATION_PATH
1141
        :type pin: string
1142
        :param user_type: the user type. The default value is
1143
          CKU_USER. You may also use CKU_SO
1144
        :type user_type: integer
1145
        """
1146
        pin1 = ckbytelist(pin)
1✔
1147
        rv = self.lib.C_Login(self.session, user_type, pin1)
1✔
1148
        if rv != CKR_OK:
1✔
1149
            raise PyKCS11Error(rv)
1✔
1150

1151
    def logout(self):
1✔
1152
        """
1153
        C_Logout
1154
        """
1155
        rv = self.lib.C_Logout(self.session)
1✔
1156
        if rv != CKR_OK:
1✔
1157
            raise PyKCS11Error(rv)
×
1158

1159
        del self
1✔
1160

1161
    def initPin(self, pin):
1✔
1162
        """
1163
        C_InitPIN
1164

1165
        :param pin: new PIN
1166
        """
1167
        new_pin1 = ckbytelist(pin)
1✔
1168
        rv = self.lib.C_InitPIN(self.session, new_pin1)
1✔
1169
        if rv != CKR_OK:
1✔
1170
            raise PyKCS11Error(rv)
×
1171

1172
    def setPin(self, old_pin, new_pin):
1✔
1173
        """
1174
        C_SetPIN
1175

1176
        :param old_pin: old PIN
1177
        :param new_pin: new PIN
1178
        """
1179
        old_pin1 = ckbytelist(old_pin)
1✔
1180
        new_pin1 = ckbytelist(new_pin)
1✔
1181
        rv = self.lib.C_SetPIN(self.session, old_pin1, new_pin1)
1✔
1182
        if rv != CKR_OK:
1✔
1183
            raise PyKCS11Error(rv)
×
1184

1185
    def createObject(self, template):
1✔
1186
        """
1187
        C_CreateObject
1188

1189
        :param template: object template
1190
        """
1191
        attrs = self._template2ckattrlist(template)
1✔
1192
        handle = PyKCS11.LowLevel.CK_OBJECT_HANDLE()
1✔
1193
        rv = self.lib.C_CreateObject(self.session, attrs, handle)
1✔
1194
        if rv != PyKCS11.CKR_OK:
1✔
1195
            raise PyKCS11.PyKCS11Error(rv)
×
1196
        return handle
1✔
1197

1198
    def destroyObject(self, obj):
1✔
1199
        """
1200
        C_DestroyObject
1201

1202
        :param obj: object ID
1203
        """
1204
        rv = self.lib.C_DestroyObject(self.session, obj)
1✔
1205
        if rv != CKR_OK:
1✔
1206
            raise PyKCS11Error(rv)
×
1207

1208
    def digestSession(self, mecha=MechanismSHA1):
1✔
1209
        """
1210
        C_DigestInit/C_DigestUpdate/C_DigestKey/C_DigestFinal
1211

1212
        :param mecha: the digesting mechanism to be used
1213
          (use `MechanismSHA1` for `CKM_SHA_1`)
1214
        :type mecha: :class:`Mechanism`
1215
        :return: A :class:`DigestSession` object
1216
        :rtype: DigestSession
1217
        """
1218
        return DigestSession(self.lib, self.session, mecha)
1✔
1219

1220
    def digest(self, data, mecha=MechanismSHA1):
1✔
1221
        """
1222
        C_DigestInit/C_Digest
1223

1224
        :param data: the data to be digested
1225
        :type data:  (binary) sring or list/tuple of bytes
1226
        :param mecha: the digesting mechanism to be used
1227
          (use `MechanismSHA1` for `CKM_SHA_1`)
1228
        :type mecha: :class:`Mechanism`
1229
        :return: the computed digest
1230
        :rtype: list of bytes
1231

1232
        :note: the returned value is an istance of :class:`ckbytelist`.
1233
          You can easly convert it to a binary string with:
1234
          ``bytes(ckbytelistDigest)``
1235
          or, for Python 2:
1236
          ``''.join(chr(i) for i in ckbytelistDigest)``
1237

1238
        """
1239
        digest = ckbytelist()
1✔
1240
        m = mecha.to_native()
1✔
1241
        data1 = ckbytelist(data)
1✔
1242
        rv = self.lib.C_DigestInit(self.session, m)
1✔
1243
        if rv != CKR_OK:
1✔
1244
            raise PyKCS11Error(rv)
×
1245
        # first call get digest size
1246
        rv = self.lib.C_Digest(self.session, data1, digest)
1✔
1247
        if rv != CKR_OK:
1✔
1248
            raise PyKCS11Error(rv)
×
1249
        # second call get actual digest data
1250
        rv = self.lib.C_Digest(self.session, data1, digest)
1✔
1251
        if rv != CKR_OK:
1✔
1252
            raise PyKCS11Error(rv)
×
1253
        return digest
1✔
1254

1255
    def sign(self, key, data, mecha=MechanismRSAPKCS1):
1✔
1256
        """
1257
        C_SignInit/C_Sign
1258

1259
        :param key: a key handle, obtained calling :func:`findObjects`.
1260
        :type key: integer
1261
        :param data: the data to be signed
1262
        :type data:  (binary) string or list/tuple of bytes
1263
        :param mecha: the signing mechanism to be used
1264
          (use `MechanismRSAPKCS1` for `CKM_RSA_PKCS`)
1265
        :type mecha: :class:`Mechanism`
1266
        :return: the computed signature
1267
        :rtype: list of bytes
1268

1269
        :note: the returned value is an instance of :class:`ckbytelist`.
1270
          You can easly convert it to a binary string with:
1271
          ``bytes(ckbytelistSignature)``
1272
          or, for Python 2:
1273
          ``''.join(chr(i) for i in ckbytelistSignature)``
1274

1275
        """
1276
        m = mecha.to_native()
1✔
1277
        signature = ckbytelist()
1✔
1278
        data1 = ckbytelist(data)
1✔
1279
        rv = self.lib.C_SignInit(self.session, m, key)
1✔
1280
        if rv != CKR_OK:
1✔
1281
            raise PyKCS11Error(rv)
×
1282
        # first call get signature size
1283
        rv = self.lib.C_Sign(self.session, data1, signature)
1✔
1284
        if rv != CKR_OK:
1✔
1285
            raise PyKCS11Error(rv)
×
1286
        # second call get actual signature data
1287
        rv = self.lib.C_Sign(self.session, data1, signature)
1✔
1288
        if rv != CKR_OK:
1✔
1289
            raise PyKCS11Error(rv)
×
1290
        return signature
1✔
1291

1292
    def verify(self, key, data, signature, mecha=MechanismRSAPKCS1):
1✔
1293
        """
1294
        C_VerifyInit/C_Verify
1295

1296
        :param key: a key handle, obtained calling :func:`findObjects`.
1297
        :type key: integer
1298
        :param data: the data that was signed
1299
        :type data:  (binary) string or list/tuple of bytes
1300
        :param signature: the signature to be verified
1301
        :type signature:  (binary) string or list/tuple of bytes
1302
        :param mecha: the signing mechanism to be used
1303
          (use `MechanismRSAPKCS1` for `CKM_RSA_PKCS`)
1304
        :type mecha: :class:`Mechanism`
1305
        :return: True if signature is valid, False otherwise
1306
        :rtype: bool
1307

1308
        """
1309
        m = mecha.to_native()
1✔
1310
        data1 = ckbytelist(data)
1✔
1311
        rv = self.lib.C_VerifyInit(self.session, m, key)
1✔
1312
        if rv != CKR_OK:
1✔
1313
            raise PyKCS11Error(rv)
×
1314
        rv = self.lib.C_Verify(self.session, data1, signature)
1✔
1315
        if rv == CKR_OK:
1✔
1316
            return True
1✔
1317
        elif rv == CKR_SIGNATURE_INVALID:
×
1318
            return False
×
1319
        else:
1320
            raise PyKCS11Error(rv)
×
1321

1322
    def encrypt(self, key, data, mecha=MechanismRSAPKCS1):
1✔
1323
        """
1324
        C_EncryptInit/C_Encrypt
1325

1326
        :param key: a key handle, obtained calling :func:`findObjects`.
1327
        :type key: integer
1328
        :param data: the data to be encrypted
1329
        :type data:  (binary) string or list/tuple of bytes
1330
        :param mecha: the encryption mechanism to be used
1331
          (use `MechanismRSAPKCS1` for `CKM_RSA_PKCS`)
1332
        :type mecha: :class:`Mechanism`
1333
        :return: the encrypted data
1334
        :rtype: list of bytes
1335

1336
        :note: the returned value is an instance of :class:`ckbytelist`.
1337
          You can easly convert it to a binary string with:
1338
          ``bytes(ckbytelistEncrypted)``
1339
          or, for Python 2:
1340
          ``''.join(chr(i) for i in ckbytelistEncrypted)``
1341

1342
        """
1343
        encrypted = ckbytelist()
1✔
1344
        m = mecha.to_native()
1✔
1345
        data1 = ckbytelist(data)
1✔
1346
        rv = self.lib.C_EncryptInit(self.session, m, key)
1✔
1347
        if rv != CKR_OK:
1✔
1348
            raise PyKCS11Error(rv)
×
1349
        # first call get encrypted size
1350
        rv = self.lib.C_Encrypt(self.session, data1, encrypted)
1✔
1351
        if rv != CKR_OK:
1✔
1352
            raise PyKCS11Error(rv)
×
1353
        # second call get actual encrypted data
1354
        rv = self.lib.C_Encrypt(self.session, data1, encrypted)
1✔
1355
        if rv != CKR_OK:
1✔
1356
            raise PyKCS11Error(rv)
×
1357
        return encrypted
1✔
1358

1359
    def decrypt(self, key, data, mecha=MechanismRSAPKCS1):
1✔
1360
        """
1361
        C_DecryptInit/C_Decrypt
1362

1363
        :param key: a key handle, obtained calling :func:`findObjects`.
1364
        :type key: integer
1365
        :param data: the data to be decrypted
1366
        :type data:  (binary) string or list/tuple of bytes
1367
        :param mecha: the decrypt mechanism to be used
1368
        :type mecha: :class:`Mechanism` instance or :class:`MechanismRSAPKCS1`
1369
          for CKM_RSA_PKCS
1370
        :return: the decrypted data
1371
        :rtype: list of bytes
1372

1373
        :note: the returned value is an instance of :class:`ckbytelist`.
1374
          You can easly convert it to a binary string with:
1375
          ``bytes(ckbytelistData)``
1376
          or, for Python 2:
1377
          ``''.join(chr(i) for i in ckbytelistData)``
1378

1379
        """
1380
        m = mecha.to_native()
1✔
1381
        decrypted = ckbytelist()
1✔
1382
        data1 = ckbytelist(data)
1✔
1383
        rv = self.lib.C_DecryptInit(self.session, m, key)
1✔
1384
        if rv != CKR_OK:
1✔
1385
            raise PyKCS11Error(rv)
×
1386
        # first call get decrypted size
1387
        rv = self.lib.C_Decrypt(self.session, data1, decrypted)
1✔
1388
        if rv != CKR_OK:
1✔
1389
            raise PyKCS11Error(rv)
×
1390
        # second call get actual decrypted data
1391
        rv = self.lib.C_Decrypt(self.session, data1, decrypted)
1✔
1392
        if rv != CKR_OK:
1✔
1393
            raise PyKCS11Error(rv)
×
1394
        return decrypted
1✔
1395

1396
    def wrapKey(self, wrappingKey, key, mecha=MechanismRSAPKCS1):
1✔
1397
        """
1398
        C_WrapKey
1399

1400
        :param wrappingKey: a wrapping key handle
1401
        :type wrappingKey: integer
1402
        :param key: a handle of the key to be wrapped
1403
        :type key: integer
1404
        :param mecha: the encrypt mechanism to be used
1405
          (use `MechanismRSAPKCS1` for `CKM_RSA_PKCS`)
1406
        :type mecha: :class:`Mechanism`
1407
        :return: the wrapped key bytes
1408
        :rtype: list of bytes
1409

1410
        :note: the returned value is an instance of :class:`ckbytelist`.
1411
          You can easily convert it to a binary string with:
1412
          ``bytes(ckbytelistData)``
1413
          or, for Python 2:
1414
          ``''.join(chr(i) for i in ckbytelistData)``
1415

1416
        """
1417
        wrapped = ckbytelist()
1✔
1418
        native = mecha.to_native()
1✔
1419
        # first call get wrapped size
1420
        rv = self.lib.C_WrapKey(self.session, native, wrappingKey, key, wrapped)
1✔
1421
        if rv != CKR_OK:
1✔
1422
            raise PyKCS11Error(rv)
×
1423
        # second call get actual wrapped key data
1424
        rv = self.lib.C_WrapKey(self.session, native, wrappingKey, key, wrapped)
1✔
1425
        if rv != CKR_OK:
1✔
1426
            raise PyKCS11Error(rv)
×
1427
        return wrapped
1✔
1428

1429
    def unwrapKey(self, unwrappingKey, wrappedKey, template, mecha=MechanismRSAPKCS1):
1✔
1430
        """
1431
        C_UnwrapKey
1432

1433
        :param unwrappingKey: the unwrapping key handle
1434
        :type unwrappingKey: integer
1435
        :param wrappedKey: the bytes of the wrapped key
1436
        :type wrappedKey:  (binary) string or list/tuple of bytes
1437
        :param template: template for the unwrapped key
1438
        :param mecha: the decrypt mechanism to be used (use
1439
          `MechanismRSAPKCS1` for `CKM_RSA_PKCS`)
1440
        :type mecha: :class:`Mechanism`
1441
        :return: the unwrapped key object
1442
        :rtype: integer
1443

1444
        """
1445
        m = mecha.to_native()
1✔
1446
        data1 = ckbytelist(wrappedKey)
1✔
1447
        handle = PyKCS11.LowLevel.CK_OBJECT_HANDLE()
1✔
1448
        attrs = self._template2ckattrlist(template)
1✔
1449
        rv = self.lib.C_UnwrapKey(self.session, m, unwrappingKey, data1, attrs, handle)
1✔
1450
        if rv != CKR_OK:
1✔
1451
            raise PyKCS11Error(rv)
×
1452
        return handle
1✔
1453

1454
    def deriveKey(self, baseKey, template, mecha):
1✔
1455
        """
1456
        C_DeriveKey
1457

1458
        :param baseKey: the base key handle
1459
        :type baseKey: integer
1460
        :param template: template for the unwrapped key
1461
        :param mecha: the decrypt mechanism to be used (use
1462
          `ECDH1_DERIVE_Mechanism(...)` for `CKM_ECDH1_DERIVE`)
1463
        :type mecha: :class:`Mechanism`
1464
        :return: the unwrapped key object
1465
        :rtype: integer
1466
        """
1467
        m = mecha.to_native()
1✔
1468
        handle = PyKCS11.LowLevel.CK_OBJECT_HANDLE()
1✔
1469
        attrs = self._template2ckattrlist(template)
1✔
1470
        rv = self.lib.C_DeriveKey(self.session, m, baseKey, attrs, handle)
1✔
1471
        if rv != CKR_OK:
1✔
1472
            raise PyKCS11Error(rv)
×
1473
        return handle
1✔
1474

1475
    def isNum(self, type):
1✔
1476
        """
1477
        is the type a numerical value?
1478

1479
        :param type: PKCS#11 type like `CKA_CERTIFICATE_TYPE`
1480
        :rtype: bool
1481
        """
1482
        if type in (
1✔
1483
            CKA_CERTIFICATE_TYPE,
1484
            CKA_CLASS,
1485
            CKA_HW_FEATURE_TYPE,
1486
            CKA_KEY_GEN_MECHANISM,
1487
            CKA_KEY_TYPE,
1488
            CKA_MODULUS_BITS,
1489
            CKA_VALUE_BITS,
1490
            CKA_VALUE_LEN,
1491
        ):
1492
            return True
1✔
1493
        return False
1✔
1494

1495
    def isString(self, type):
1✔
1496
        """
1497
        is the type a string value?
1498

1499
        :param type: PKCS#11 type like `CKA_LABEL`
1500
        :rtype: bool
1501
        """
1502
        if type in (CKA_LABEL, CKA_APPLICATION):
1✔
1503
            return True
1✔
1504
        return False
1✔
1505

1506
    def isBool(self, type):
1✔
1507
        """
1508
        is the type a boolean value?
1509

1510
        :param type: PKCS#11 type like `CKA_ALWAYS_SENSITIVE`
1511
        :rtype: bool
1512
        """
1513
        if type in (
1✔
1514
            CKA_ALWAYS_AUTHENTICATE,
1515
            CKA_ALWAYS_SENSITIVE,
1516
            CKA_DECRYPT,
1517
            CKA_DERIVE,
1518
            CKA_ENCRYPT,
1519
            CKA_EXTRACTABLE,
1520
            CKA_HAS_RESET,
1521
            CKA_LOCAL,
1522
            CKA_MODIFIABLE,
1523
            CKA_COPYABLE,
1524
            CKA_DESTROYABLE,
1525
            CKA_NEVER_EXTRACTABLE,
1526
            CKA_PRIVATE,
1527
            CKA_RESET_ON_INIT,
1528
            CKA_SECONDARY_AUTH,
1529
            CKA_SENSITIVE,
1530
            CKA_SIGN,
1531
            CKA_SIGN_RECOVER,
1532
            CKA_TOKEN,
1533
            CKA_TRUSTED,
1534
            CKA_UNWRAP,
1535
            CKA_VERIFY,
1536
            CKA_VERIFY_RECOVER,
1537
            CKA_WRAP,
1538
            CKA_WRAP_WITH_TRUSTED,
1539
        ):
1540
            return True
1✔
1541
        return False
1✔
1542

1543
    def isBin(self, type):
1✔
1544
        """
1545
        is the type a byte array value?
1546

1547
        :param type: PKCS#11 type like `CKA_MODULUS`
1548
        :rtype: bool
1549
        """
1550
        return (
1✔
1551
            (not self.isBool(type))
1552
            and (not self.isString(type))
1553
            and (not self.isNum(type))
1554
        )
1555

1556
    def isAttributeList(self, type):
1✔
1557
        """
1558
        is the type a attribute list value?
1559

1560
        :param type: PKCS#11 type like `CKA_WRAP_TEMPLATE`
1561
        :rtype: bool
1562
        """
1563
        if type in (CKA_WRAP_TEMPLATE, CKA_UNWRAP_TEMPLATE):
1✔
1564
            return True
1✔
1565
        return False
1✔
1566

1567
    def _template2ckattrlist(self, template):
1✔
1568
        t = PyKCS11.LowLevel.ckattrlist(len(template))
1✔
1569
        for x in range(len(template)):
1✔
1570
            attr = template[x]
1✔
1571
            if self.isNum(attr[0]):
1✔
1572
                t[x].SetNum(attr[0], int(attr[1]))
1✔
1573
            elif self.isString(attr[0]):
1✔
1574
                t[x].SetString(attr[0], str(attr[1]))
1✔
1575
            elif self.isBool(attr[0]):
1✔
1576
                t[x].SetBool(attr[0], attr[1] == CK_TRUE)
1✔
1577
            elif self.isAttributeList(attr[0]):
1✔
1578
                t[x].SetList(attr[0], self._template2ckattrlist(attr[1]))
1✔
1579
            elif self.isBin(attr[0]):
1✔
1580
                attrBin = attr[1]
1✔
1581
                attrStr = attr[1]
1✔
1582
                if isinstance(attr[1], int):
1✔
1583
                    attrStr = str(attr[1])
×
1584
                if isinstance(attr[1], bytes):
1✔
1585
                    attrBin = ckbytelist(attrStr)
1✔
1586
                t[x].SetBin(attr[0], attrBin)
1✔
1587
            else:
1588
                raise PyKCS11Error(-2)
×
1589
        return t
1✔
1590

1591
    def generateKey(self, template, mecha=MechanismAESGENERATEKEY):
1✔
1592
        """
1593
        generate a secret key
1594

1595
        :param template: template for the secret key
1596
        :param mecha: mechanism to use
1597
        :return: handle of the generated key
1598
        :rtype: PyKCS11.LowLevel.CK_OBJECT_HANDLE
1599
        """
1600
        t = self._template2ckattrlist(template)
1✔
1601
        ck_handle = PyKCS11.LowLevel.CK_OBJECT_HANDLE()
1✔
1602
        m = mecha.to_native()
1✔
1603
        rv = self.lib.C_GenerateKey(self.session, m, t, ck_handle)
1✔
1604
        if rv != CKR_OK:
1✔
1605
            raise PyKCS11Error(rv)
×
1606
        return ck_handle
1✔
1607

1608
    def generateKeyPair(
1✔
1609
        self, templatePub, templatePriv, mecha=MechanismRSAGENERATEKEYPAIR
1610
    ):
1611
        """
1612
        generate a key pair
1613

1614
        :param templatePub: template for the public key
1615
        :param templatePriv:  template for the private key
1616
        :param mecha: mechanism to use
1617
        :return: a tuple of handles (pub, priv)
1618
        :rtype: tuple
1619
        """
1620
        tPub = self._template2ckattrlist(templatePub)
1✔
1621
        tPriv = self._template2ckattrlist(templatePriv)
1✔
1622
        ck_pub_handle = PyKCS11.LowLevel.CK_OBJECT_HANDLE()
1✔
1623
        ck_prv_handle = PyKCS11.LowLevel.CK_OBJECT_HANDLE()
1✔
1624
        m = mecha.to_native()
1✔
1625
        rv = self.lib.C_GenerateKeyPair(
1✔
1626
            self.session, m, tPub, tPriv, ck_pub_handle, ck_prv_handle
1627
        )
1628

1629
        if rv != CKR_OK:
1✔
1630
            raise PyKCS11Error(rv)
1✔
1631
        return ck_pub_handle, ck_prv_handle
1✔
1632

1633
    def findObjects(self, template=()):
1✔
1634
        """
1635
        find the objects matching the template pattern
1636

1637
        :param template: list of attributes tuples (attribute,value).
1638
          The default value is () and all the objects are returned
1639
        :type template: list
1640
        :return: a list of object ids
1641
        :rtype: list
1642
        """
1643
        t = self._template2ckattrlist(template)
1✔
1644

1645
        # we search for 10 objects by default. speed/memory tradeoff
1646
        result = PyKCS11.LowLevel.ckobjlist(10)
1✔
1647

1648
        rv = self.lib.C_FindObjectsInit(self.session, t)
1✔
1649
        if rv != CKR_OK:
1✔
1650
            raise PyKCS11Error(rv)
×
1651

1652
        res = []
1✔
1653
        while True:
1✔
1654
            rv = self.lib.C_FindObjects(self.session, result)
1✔
1655
            if rv != CKR_OK:
1✔
1656
                raise PyKCS11Error(rv)
×
1657
            for x in result:
1✔
1658
                # make a copy of the handle: the original value get
1659
                # corrupted (!!)
1660
                a = CK_OBJECT_HANDLE(self)
1✔
1661
                a.assign(x.value())
1✔
1662
                res.append(a)
1✔
1663
            if len(result) == 0:
1✔
1664
                break
1✔
1665

1666
        rv = self.lib.C_FindObjectsFinal(self.session)
1✔
1667
        if rv != CKR_OK:
1✔
1668
            raise PyKCS11Error(rv)
×
1669
        return res
1✔
1670

1671
    def getAttributeValue(self, obj_id, attr, allAsBinary=False):
1✔
1672
        """
1673
        C_GetAttributeValue
1674

1675
        :param obj_id: object ID returned by :func:`findObjects`
1676
        :type obj_id: integer
1677
        :param attr: list of attributes
1678
        :type attr: list
1679
        :param allAsBinary: return all values as binary data; default is False.
1680
        :type allAsBinary: Boolean
1681
        :return: a list of values corresponding to the list of attributes
1682
        :rtype: list
1683

1684
        :see: :func:`getAttributeValue_fragmented`
1685

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

1689
          Binary data is returned as :class:`ckbytelist` type, usable
1690
          as a list containing only bytes.
1691
          You can easly convert it to a binary string with:
1692
          ``bytes(ckbytelistVariable)``
1693
          or, for Python 2:
1694
          ``''.join(chr(i) for i in ckbytelistVariable)``
1695

1696
        """
1697
        valTemplate = PyKCS11.LowLevel.ckattrlist(len(attr))
1✔
1698
        for x in range(len(attr)):
1✔
1699
            valTemplate[x].SetType(attr[x])
1✔
1700
        # first call to get the attribute size and reserve the memory
1701
        rv = self.lib.C_GetAttributeValue(self.session, obj_id, valTemplate)
1✔
1702
        if rv in (
1✔
1703
            CKR_ATTRIBUTE_TYPE_INVALID,
1704
            CKR_ATTRIBUTE_SENSITIVE,
1705
            CKR_ARGUMENTS_BAD,
1706
        ):
1707
            return self.getAttributeValue_fragmented(obj_id, attr, allAsBinary)
1✔
1708

1709
        if rv != CKR_OK:
1✔
1710
            raise PyKCS11Error(rv)
×
1711
        # second call to get the attribute value
1712
        rv = self.lib.C_GetAttributeValue(self.session, obj_id, valTemplate)
1✔
1713
        if rv != CKR_OK:
1✔
1714
            raise PyKCS11Error(rv)
×
1715

1716
        res = []
1✔
1717
        for x in range(len(attr)):
1✔
1718
            if allAsBinary:
1✔
1719
                res.append(valTemplate[x].GetBin())
1✔
1720
            elif valTemplate[x].IsNum():
1✔
1721
                res.append(valTemplate[x].GetNum())
1✔
1722
            elif valTemplate[x].IsBool():
1✔
1723
                res.append(valTemplate[x].GetBool())
1✔
1724
            elif valTemplate[x].IsString():
1✔
1725
                res.append(valTemplate[x].GetString())
1✔
1726
            elif valTemplate[x].IsBin():
1✔
1727
                res.append(valTemplate[x].GetBin())
1✔
1728
            else:
1729
                raise PyKCS11Error(-2)
×
1730

1731
        return res
1✔
1732

1733
    def getAttributeValue_fragmented(self, obj_id, attr, allAsBinary=False):
1✔
1734
        """
1735
        Same as :func:`getAttributeValue` except that when some attribute
1736
        is sensitive or unknown an empty value (None) is returned.
1737

1738
        Note: this is achived by getting attributes one by one.
1739

1740
        :see: :func:`getAttributeValue`
1741
        """
1742
        # some attributes does not exists or is sensitive
1743
        # but we don't know which ones. So try one by one
1744
        valTemplate = PyKCS11.LowLevel.ckattrlist(1)
1✔
1745
        res = []
1✔
1746
        for x in range(len(attr)):
1✔
1747
            valTemplate[0].Reset()
1✔
1748
            valTemplate[0].SetType(attr[x])
1✔
1749
            # first call to get the attribute size and reserve the memory
1750
            rv = self.lib.C_GetAttributeValue(self.session, obj_id, valTemplate)
1✔
1751
            if rv in (
1✔
1752
                CKR_ATTRIBUTE_TYPE_INVALID,
1753
                CKR_ATTRIBUTE_SENSITIVE,
1754
                CKR_ARGUMENTS_BAD,
1755
            ):
1756
                # append an empty value
1757
                res.append(None)
1✔
1758
                continue
1✔
1759

1760
            if rv != CKR_OK:
1✔
1761
                raise PyKCS11Error(rv)
×
1762
            # second call to get the attribute value
1763
            rv = self.lib.C_GetAttributeValue(self.session, obj_id, valTemplate)
1✔
1764
            if rv != CKR_OK:
1✔
1765
                raise PyKCS11Error(rv)
×
1766

1767
            if allAsBinary:
1✔
1768
                res.append(valTemplate[0].GetBin())
×
1769
            elif valTemplate[0].IsNum():
1✔
1770
                res.append(valTemplate[0].GetNum())
1✔
1771
            elif valTemplate[0].IsBool():
1✔
1772
                res.append(valTemplate[0].GetBool())
1✔
1773
            elif valTemplate[0].IsString():
1✔
1774
                res.append(valTemplate[0].GetString())
1✔
1775
            elif valTemplate[0].IsBin():
1✔
1776
                res.append(valTemplate[0].GetBin())
1✔
1777
            elif valTemplate[0].IsAttributeList():
1✔
1778
                res.append(valTemplate[0].GetBin())
1✔
1779
            else:
1780
                raise PyKCS11Error(-2)
×
1781

1782
        return res
1✔
1783

1784
    def setAttributeValue(self, obj_id, template):
1✔
1785
        """
1786
        C_SetAttributeValue
1787

1788
        :param obj_id: object ID returned by :func:`findObjects`
1789
        :type obj_id: integer
1790
        :param template: list of (attribute, value) pairs
1791
        :type template: list
1792
        :return: Nothing
1793
        :rtype: None
1794
        """
1795

1796
        templ = self._template2ckattrlist(template)
1✔
1797
        rv = self.lib.C_SetAttributeValue(self.session, obj_id, templ)
1✔
1798

1799
        if rv != CKR_OK:
1✔
1800
            raise PyKCS11Error(rv)
×
1801

1802
        return None
1✔
1803

1804
    def seedRandom(self, seed):
1✔
1805
        """
1806
        C_SeedRandom
1807

1808
        :param seed: seed material
1809
        :type seed: iterable
1810
        """
1811
        low_seed = ckbytelist(seed)
1✔
1812
        rv = self.lib.C_SeedRandom(self.session, low_seed)
1✔
1813
        if rv != CKR_OK:
1✔
1814
            raise PyKCS11Error(rv)
×
1815

1816
    def generateRandom(self, size=16):
1✔
1817
        """
1818
        C_GenerateRandom
1819

1820
        :param size: number of random bytes to get
1821
        :type size: integer
1822

1823
        :note: the returned value is an instance of :class:`ckbytelist`.
1824
          You can easly convert it to a binary string with:
1825
          ``bytes(random)``
1826
          or, for Python 2:
1827
          ``''.join(chr(i) for i in random)``
1828
        """
1829
        low_rand = ckbytelist([0] * size)
1✔
1830
        rv = self.lib.C_GenerateRandom(self.session, low_rand)
1✔
1831
        if rv != CKR_OK:
1✔
1832
            raise PyKCS11Error(rv)
×
1833
        return low_rand
1✔
1834

1835

1836
if __name__ == "__main__":
1✔
1837
    # sample test/debug code
1838
    p = PyKCS11Lib()
×
1839
    p.load()
×
1840

1841
    print("getInfo")
×
1842
    print(p.getInfo())
×
1843

1844
    print()
×
1845
    print("getSlotList")
×
1846
    s = p.getSlotList()
×
1847
    print("slots:", s)
×
1848
    slot = s[0]
×
1849
    print("using slot:", slot)
×
1850

1851
    print()
×
1852
    print("getSlotInfo")
×
1853
    print(p.getSlotInfo(slot))
×
1854

1855
    print()
×
1856
    print("getTokenInfo")
×
1857
    print(p.getTokenInfo(slot))
×
1858

1859
    print()
×
1860
    print("openSession")
×
1861
    se = p.openSession(slot)
×
1862

1863
    print()
×
1864
    print("sessionInfo")
×
1865
    print(se.getSessionInfo())
×
1866

1867
    print()
×
1868
    print("seedRandom")
×
1869
    try:
×
1870
        se.seedRandom([1, 2, 3, 4])
×
1871
    except PyKCS11Error as e:
×
1872
        print(e)
×
1873
    print("generateRandom")
×
1874
    print(se.generateRandom())
×
1875

1876
    print()
×
1877
    print("login")
×
1878
    se.login(pin="0000")
×
1879

1880
    print()
×
1881
    print("sessionInfo")
×
1882
    print(se.getSessionInfo())
×
1883

1884
    print()
×
1885
    print("findObjects")
×
1886
    objs = se.findObjects([(CKA_CLASS, CKO_CERTIFICATE)])
×
1887
    print("Nb objetcs:", len(objs))
×
1888
    print(objs)
×
1889

1890
    print()
×
1891
    print("getAttributeValue")
×
1892
    for o in objs:
×
1893
        attr = se.getAttributeValue(o, [CKA_LABEL, CKA_CLASS])
×
1894
        print(attr)
×
1895

1896
    print()
×
1897
    print("logout")
×
1898
    se.logout()
×
1899

1900
    print()
×
1901
    print("closeSession")
×
1902
    se.closeSession()
×
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