PyKCS11 provided samples: getinfo.py

I will continue the serie about PyKCS11 I started last year in PyKCS11 introduction. Just to remind you, PyKCS11 is a Python wrapper above the PKCS#11 API.

Setup


In the following I will use a .Net card and the PKCS#11 for .Net cards I blogged about in April 2010.

I generated a X509 certificate using tinyca2 and loaded it on the card using Firefox.

Example codes provided with PyKCS11: getinfo.py


getinfo.py is a short sample code to... get information from the token.

Output


cryptokiVersion: 2.20 
flags:  
libraryDescription: Gemalto .NET PKCS11              
libraryVersion: 2.2 
manufacturerID: Gemalto                          
Available Slots: 5 [0, 1, 2, 3, 4]
Slot n.: 0
 firmwareVersion: 0.00 
 flags: CKF_TOKEN_PRESENT, CKF_REMOVABLE_DEVICE, CKF_HW_SLOT 
 hardwareVersion: 0.00 
 manufacturerID: Unknown                          
 slotDescription: Gemalto GemPC Twin 00 00                                         
 SessionInfo
  flags: CKF_SERIAL_SESSION 
  slotID: 0 
  state: 0 
  ulDeviceError: 0 
 TokenInfo
  firmwareVersion: 0.0 
  flags: CKF_LOGIN_REQUIRED, CKF_TOKEN_INITIALIZED, CKF_USER_PIN_INITIALIZED 
  hardwareVersion: 0.0 
  label: .NET #AFC4963915BE14DB           
  manufacturerID: Gemalto                          
  model: .NET Card        
  serialNumber: AFC4963915BE14DB 
  ulFreePrivateMemory: -1 
  ulFreePublicMemory: -1 
  ulMaxPinLen: 0 
  ulMaxRwSessionCount: 0 
  ulMaxSessionCount: 0 
  ulMinPinLen: 0 
  ulRwSessionCount: 0 
  ulSessionCount: 1 
  ulTotalPrivateMemory: -1 
  ulTotalPublicMemory: -1 
  utcTime:  
  Mechanism list: 
   CKM_RSA_PKCS_KEY_PAIR_GEN 
    ulMinKeySize: 512 
    ulMaxKeySize: 2048 
    flags: CKF_HW, CKF_GENERATE_KEY_PAIR 
   CKM_RSA_PKCS 
    ulMinKeySize: 512 
    ulMaxKeySize: 2048 
    flags: CKF_ENCRYPT, CKF_HW, CKF_DECRYPT, CKF_SIGN, CKF_VERIFY 
   CKM_RSA_X_509 
    ulMinKeySize: 512 
    ulMaxKeySize: 2048 
    flags: CKF_ENCRYPT, CKF_HW, CKF_DECRYPT, CKF_SIGN, CKF_VERIFY 
   CKM_MD5_RSA_PKCS 
    ulMinKeySize: 512 
    ulMaxKeySize: 2048 
    flags: CKF_HW, CKF_SIGN, CKF_VERIFY 
   CKM_SHA1_RSA_PKCS 
    ulMinKeySize: 512 
    ulMaxKeySize: 2048 
    flags: CKF_HW, CKF_SIGN, CKF_VERIFY 
   CKM_SHA256_RSA_PKCS 
    ulMinKeySize: 512 
    ulMaxKeySize: 2048 
    flags: CKF_HW, CKF_SIGN, CKF_VERIFY 
   CKM_MD5 
    flags: CKF_DIGEST 
   CKM_SHA_1 
    flags: CKF_DIGEST 
   CKM_SHA256 
    flags: CKF_DIGEST 
Slot n.: 1
 firmwareVersion: 0.00 
 flags: CKF_REMOVABLE_DEVICE, CKF_HW_SLOT 
 hardwareVersion: 0.00 
 manufacturerID: Unknown                          
 slotDescription: empty                                                            
 SessionInfo Error: CKR_TOKEN_NOT_PRESENT (0x000000E0)
Slot n.: 2
 firmwareVersion: 0.00 
 flags: CKF_REMOVABLE_DEVICE, CKF_HW_SLOT 
 hardwareVersion: 0.00 
 manufacturerID: Unknown                          
 slotDescription: empty                                                            
 SessionInfo Error: CKR_TOKEN_NOT_PRESENT (0x000000E0)
Slot n.: 3
 firmwareVersion: 0.00 
 flags: CKF_REMOVABLE_DEVICE, CKF_HW_SLOT 
 hardwareVersion: 0.00 
 manufacturerID: Unknown                          
 slotDescription: empty                                                            
 SessionInfo Error: CKR_TOKEN_NOT_PRESENT (0x000000E0)
Slot n.: 4
 firmwareVersion: 0.00 
 flags: CKF_REMOVABLE_DEVICE, CKF_HW_SLOT 
 hardwareVersion: 0.00 
 manufacturerID: Unknown                          
 slotDescription: empty                                                            
 SessionInfo Error: CKR_TOKEN_NOT_PRESENT (0x000000E0)

Comments


The PKCS#11 token has 5 virtual slots. Only slot 0 contains information about the card.

The getinfo.py sample code uses colors to display the information.

Source code


A large part of the source code is to manage:
  • command arguments
  • display and colorization

We will see in a future blog article how to use the wrapper in an interactive Python. So a lot of the extra code in the sample will be useless in that case.

#!/usr/bin/env python

#   Copyright (C) 2006-2010 Ludovic Rousseau (ludovic.rousseau@free.fr)
#
# This file is free software; you can redistribute it and/or modify it
# under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
# General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA

import PyKCS11
import platform
import sys


class getInfo(object):
    red = blue = magenta = normal = ""

    def colorize(self, text, arg):
        print self.magenta + text + self.blue, arg, self.normal

    def display(self, obj, indent=""):
        dico = obj.to_dict()
        for key in sorted(dico.keys()):
            type = obj.fields[key]
            left = indent + key + ":"
            if type == "flags":
                self.colorize(left, ", ".join(dico[key]))
            elif type == "pair":
                self.colorize(left, "%d.%d" % dico[key])
            else:
                self.colorize(left, dico[key])

    def __init__(self, lib=None):
        if sys.stdout.isatty() and platform.system().lower() != 'windows':
            self.red = "\x1b[01;31m"
            self.blue = "\x1b[34m"
            self.magenta = "\x1b[35m"
            self.normal = "\x1b[0m"

        self.pkcs11 = PyKCS11.PyKCS11Lib()
        self.pkcs11.load(lib)

    def getSlotInfo(self, slot):
        print "Slot n.:", slot
        self.display(self.pkcs11.getSlotInfo(slot), " ")

    def getTokenInfo(self, slot):
        print " TokenInfo"
        self.display(self.pkcs11.getTokenInfo(slot), "  ")

    def getMechanismInfo(self, slot):
        print "  Mechanism list: "
        m = self.pkcs11.getMechanismList(slot)
        for x in m:
            self.colorize("  ", x)
            i = self.pkcs11.getMechanismInfo(slot, x)
            if not i.flags & PyKCS11.CKF_DIGEST:
                if i.ulMinKeySize != PyKCS11.CK_UNAVAILABLE_INFORMATION:
                    self.colorize("    ulMinKeySize:", i.ulMinKeySize)
                if i.ulMaxKeySize != PyKCS11.CK_UNAVAILABLE_INFORMATION:
                    self.colorize("    ulMaxKeySize:", i.ulMaxKeySize)
            self.colorize("    flags:", ", ".join(i.flags2text()))

    def getInfo(self):
        self.display(self.pkcs11.getInfo())

    def getSessionInfo(self, slot, pin=None):
        print " SessionInfo",
        session = self.pkcs11.openSession(slot)

        if pin:
            print "(using pin: %s)" % pin
            session.login(pin)
        else:
            print

        self.display(session.getSessionInfo(), "  ")

        if pin:
            session.logout()


def usage():
    print "Usage:", sys.argv[0],
    print "[-p pin][--pin=pin]",
    print "[-s slot][--slot=slot]",
    print "[-c lib][--lib=lib]",
    print "[-h][--help]",
    print "[-o][--opensession]"

if __name__ == '__main__':
    import getopt

    try:
        opts, args = getopt.getopt(sys.argv[1:], "p:s:c:ho",
            ["pin=", "slot=", "lib=", "help", "opensession"])
    except getopt.GetoptError:
        # print help information and exit:
        usage()
        sys.exit(2)

    slot = None
    lib = None
    pin = None
    open_session = False
    pin_available = False
    for o, a in opts:
        if o in ("-h", "--help"):
            usage()
            sys.exit()
        if o in ("-p", "--pin"):
            pin = a
            pin_available = True
            open_session = True
        if o in ("-s", "--slot"):
            slot = int(a)
        if o in ("-c", "--lib"):
            lib = a
        if o in ("-o", "--opensession"):
            open_session = True

    gi = getInfo(lib)
    gi.getInfo()

    slots = gi.pkcs11.getSlotList()
    print "Available Slots:", len(slots), slots

    if len(slots) == 0:
        sys.exit(2)

    if slot:
        slots = [slots[slot]]

    for slot in slots:
        try:
            gi.getSlotInfo(slot)
            gi.getSessionInfo(slot, pin)
            gi.getTokenInfo(slot)
            gi.getMechanismInfo(slot)
        except PyKCS11.PyKCS11Error, e:
            print "Error:", e

Conclusion

If you have to use a PKCS#11 token and you can choose the programming language you want then you can use Python instead of C.