Token class prototype
oo::class create token { variable libp11 variable handle variable infotok variable pintok variable nodet # constructor {handlelp11 labtoken slottoken} { global pass global yespas set handle $handlelp11 set slots [pki::pkcs11::listslots $handle] array set infotok [] foreach slotinfo $slots { set slotflags [lindex $slotinfo 2] if {[lsearch -exact $slotflags TOKEN_PRESENT] != -1} { if {[string first $labtoken [lindex $slotinfo 1]] != -1} { set infotok(slotlabel) [lindex $slotinfo 1] set infotok(slotid) [lindex $slotinfo 0] set infotok(slotflags) [lindex $slotinfo 2] set infotok(token) [lindex $slotinfo 3] # break } } } # if {[llength [array names infotok]] == 0 } { error "Constructor: Token not present for library : $handle" } # set nodet [dict create pkcs11_handle $handle] dict set nodet pkcs11_slotid $infotok(slotid) set tit " PIN- $infotok(slotlabel)" set xa [my read_password $tit] if {$xa == "no"} { error " PIN $infotok(slotlabel)" } set pintok $pass set pass "" set rr [my login ] if { $rr == 0 } { unset pintok error " PIN- $infotok(slotlabel)." } elseif {$rr == -1} { unset pintok error " ." } my logout } # method infoslot {} { return [array get infotok] } method listcerts {} { array set lcerts [] set certsder [pki::pkcs11::listcertsder $handle $infotok(slotid)] # foreach lc $certsder { array set derc $lc set lcerts($derc(pkcs11_label)) [list $derc(cert_der) $derc(pkcs11_id)] #parray derc } return [array get lcerts] } method read_password {tit} { global yespas global pass set tit_orig "$::labpas" if {$tit != ""} { set ::labpas "$tit" } tk busy hold ".st.fr1" tk busy hold ".st.fr3" # place .topPinPw -in .st.fr1.fr2_certs.labCert -relx 1.0 -rely 3.0 -relwidth 3.5 place .topPinPw -in .st.labMain -relx 0.35 -rely 5.0 -relwidth 0.30 set yespas "" focus .topPinPw.labFrPw.entryPw vwait yespas catch {tk busy forget ".st.fr1"} catch {tk busy forget ".st.fr3"} if {$tit != ""} { set ::labpas "$tit_orig" } place forget .topPinPw return $yespas } unexport read_password method rename {type ckaid newlab} { if {$type != "cert" && $type != "key" && $type != "all"} { error "Bad type for rename " } set uu $nodet lappend uu "pkcs11_id" lappend uu $ckaid lappend uu "pkcs11_label" lappend uu $newlab if { [my login ] == 0 } { unset uu return 0 } pki::pkcs11::rename $type $uu my logout return 1 } method changeid {type ckaid newid} { if {$type != "cert" && $type != "key" && $type != "all"} { error "Bad type for changeid " } set uu $nodet lappend uu "pkcs11_id" lappend uu $ckaid lappend uu "pkcs11_id_new" lappend uu $newid if { [my login ] == 0 } { unset uu return 0 } pki::pkcs11::rename $type $uu my logout return 1 } method delete {type ckaid} { if {$type != "cert" && $type != "key" && $type != "all" && $type != "obj"} { error "Bad type for delete" } set uu $nodet lappend uu "pkcs11_id" lappend uu $ckaid my login ::pki::pkcs11::delete $type $uu my logout return 1 } method deleteobj {hobj} { set uu $nodet lappend uu "hobj" lappend uu $hobj #tk_messageBox -title "class deleteobj" -icon info -message "hobj: $hobj\n" -detail "$uu" return [::pki::pkcs11::delete obj $uu ] } method listmechs {} { set llmech [pki::pkcs11::listmechs $handle $infotok(slotid)] return $llmech } method pubkeyinfo {cert_der_hex} { array set linfopk [pki::pkcs11::pubkeyinfo $cert_der_hex $nodet] return [array get linfopk] } method listobjects {type} { if {$type != "cert" && $type != "pubkey" && $type != "privkey" && $type != "all" && $type != "data"} { error "Bad type for listobjects " } set allobjs [::pki::pkcs11::listobjects $handle $infotok(slotid) $type] return $allobjs } method importcert {cert_der_hex cka_label} { set uu $nodet dict set uu pkcs11_label $cka_label if {[catch {set pkcs11id [pki::pkcs11::importcert $cert_der_hex $uu]} res] } { error "Cannot import this certificate:$res" # return 0 } return $pkcs11id } method login {} { set wh 1 set rl -1 while {$wh == 1} { if {[catch {set rl [pki::pkcs11::login $handle $infotok(slotid) $pintok]} res]} { if {[string first "SESSION_HANDLE_INVALID" $res] != -1} { pki::pkcs11::closesession $handle continue } elseif {[string first "TOKEN_NOT_PRESENT" $res] != -1} { set wh 0 continue } } break } if {$wh == 0} { return -1 } return $rl } method logout {} { return [pki::pkcs11::logout $handle $infotok(slotid)] } method keypair {typegost parkey} { my login set skey [pki::pkcs11::keypair $typegost $parkey $nodet] my logout return $skey } method digest {typehash source} { return [pki::pkcs11::digest $typehash $source $nodet] } method signkey {ckm digest hobj_priv} { set uu $nodet dict set uu hobj_privkey $hobj_priv my login set ss [pki::pkcs11::sign $ckm $digest $uu] my logout return $ss } method signcert {ckm digest pkcs11_id} { set uu $nodet dict set uu pkcs11_id $pkcs11_id my login set ss [pki::pkcs11::sign $ckm $digest $uu] my logout return $ss } method verify {digest signature asn1pubkey} { set uu $nodet dict set uu pubkeyinfo $asn1pubkey return [pki::pkcs11::verify $digest $signature $uu] } method tokenpresent {} { set slots [pki::pkcs11::listslots $handle] foreach slotinfo $slots { set slotid [lindex $slotinfo 0] set slotlabel [lindex $slotinfo 1] set slotflags [lindex $slotinfo 2] if {[lsearch -exact $slotflags TOKEN_PRESENT] != -1} { if {infotok(slotlabel) == $slotlabel && $slotid == $infotok(slotid)} { return 1 } } } return 0 } method setpin {type tpin newpin} { if {$type != "user" && $type != "so"} { return 0 } if {$type == "user"} { if {$tpin != $pintok} { return 0 } } set ret [::pki::pkcs11::setpin $handle $infotok(slotid) $type $tpin $newpin] if {$type == "user"} { if {$ret} { set pitok $newpin } } return $ret } method inituserpin {sopin upin} { set ret [::pki::pkcs11::inituserpin $handle $infotok(slotid) $sopin $upin] return $ret } method importkey {uukey} { set uu $nodet append uu " $uukey" my login if {[catch {set impkey [pki::pkcs11::importkey $uu ]} res] } { set impkey 0 } my logout return $impkey } # destructor { variable handle if {[info exists pintok]} { my login } # ::pki::pkcs11::unloadmodule $handle } }
The Token class, like any class in Python , includes a constructor, methods, and a destructor. Constructor and destructor are the same methods, only with predefined names. The constructor is named __init__, and the destructor is named __del__. Constructor and destructor declarations can be omitted. And in the Token class, we will omit the destructor declaration, but the constructor will be needed. The constructor will instantiate the Token class for a specific token with specific attributes.
I. Token class constructor
So, the constructor of the Token class looks like this:
import sys
import pyp11
class Token:
def __init__ (self, handlelp11, slottoken, serialnum):
flags = ''
self.pyver = sys.version[0]
if (self.pyver == '2'):
print (' python3')
quit()
# handle PKCS#11
self.handle = handlelp11
#
self.slotid = slottoken
#
self.sn = serialnum
#
ret, stat = self.tokinfo()
#
if (stat != ''):
#
self.returncode = stat
return
# ()
The parameters of the constructor (of the __init__ method) are (in addition to the mandatory self) the handle of the token library (handlelp11), the slot number (slottoken) in which the token should be located, and the token serial number (serialnum).
To get the handle of the pkcs # 11 library, slot numbers and information about the tokens in them, you can use the following script:
#!/usr/bin/python3
import sys
import pyp11
from Token import Token
def listslots (handle):
slots = pyp11.listslots(aa)
i = 0
lslots = []
for v in slots:
for f in v[2]:
if (f == 'TOKEN_PRESENT'):
i = 1
lslots.append(v)
break
i += 1
return (lslots)
# Linux
#
lib = '/usr/local/lib64/libls11sw2016.so'
#
#lib = '/usr/local/lib64/libls11cloud.so'
#
#lib = '/usr/local/lib64/librtpkcs11ecp_2.0.so'
# Windows
#lib='C:\Temp\ls11sw2016.dll'
try:
# handle ( )
aa = pyp11.loadmodule(lib)
print('Handle ' + lib + ': ' + aa)
except:
print('Except load lib: ')
e = sys.exc_info()[1]
e1 = e.args[0]
#
print (e1)
quit()
#
slots = listslots(aa)
i = 0
for v in slots:
for f in v[2]:
if (f == 'TOKEN_PRESENT'):
if (i == 0):
print ('\n \n')
it = v[3]
print ('slotid=' + str(v[0]))
print ('\tFlags=' + str(v[2]))
print ('\tLabel="' + it[0].strip() + '"')
print ('\tManufacturer="' + it[1].strip() + '"')
print ('\tModel="' + it[2].strip() + '"')
print ('\tSerialNumber="' + it[3].strip() + '"')
i = 1
break
i += 1
pyp11.unloadmodule(aa)
if (i == 0):
print (' . ')
quit()
If everything is clear with the library and the slot, then the question may arise with the serial number of the token - why this parameter is needed and why exactly it, but, for example, not the token label. Let's make a reservation right away that this is fundamental for retrievable tokens, when an attacker (or accidentally) replaces one token in the slot with another token. Moreover, different instances of a token can have the same labels. And finally, the token may not have been initialized yet, or the owner will reinitialize it, in particular, change the token label. In theory, even a serial number does not guarantee its identity; it is optimal to take into account all information about a token (serial number, model, manufacturer). It is the task of the constructor to store the arguments of the token in the variables of the created instance of the class:
...
# handle PKCS#11
self.handle = handlelp11
#
self.slotid = slottoken
#
self.sn = serialnum
...
The tokinfo () method defined in this class checks for the presence of the specified token in the specified slot .
The tokinfo method returns two values โโ(see above in the constructor):
#
ret, stat = self.tokinfo()
The first variable (ret) contains the result of the method execution, and the second (stat) contains information about how the execution of the method ended. If the second variable is empty, then the tokinfo method succeeded. If the second variable is not empty, then the method has ended with an error. The error information will be in this variable. When an error is encountered during the execution of the self.tokinfo method, the constructor writes it to the returncode variable:
#
ret, stat = self.tokinfo()
#
if (stat != ''):
# returncode
self.returncode = stat
return
After creating an object (class instance), you need to check the value of the returncode variable to make sure that the object for the specified token has been created:
#!/usr/bin/python3
import sys
import pyp11
from Token import Token
#
#
lib = '/usr/local/lib64/librtpkcs11ecp_2.0.so'
try:
aa = pyp11.loadmodule(lib)
except:
e = sys.exc_info()[1]
e1 = e.args[0]
print (e1)
quit()
#
sn = '9999999999999999'
slot = 110
#
t1 = Token(aa, slot, sn)
# returncode
if (t1.returncode != ''):
#
print (t1.returncode)
#
del t1
#
quit()
#
. . .
If an error is detected while creating an object, then it is advisable to destroy this object:
del < >
II. Method architecture in the Token class
The main principle when writing methods was that exception handling was inside the methods, and information about exceptions (errors) was returned in text form. Based on this, all methods return two values: the actual execution result and information about the error. If there are no errors, then the second value is empty. We have already seen this using the tokinfo method in the constructor. And here is the code for the tokinfo method itself:
def tokinfo(self):
status = ''
#
try:
slots = pyp11.listslots(self.handle)
except:
#
e = sys.exc_info()[1]
e1 = e.args[0]
dd = ''
status = e1
return (dd, status)
status = ''
#
#
for v in slots:
#
if (v[0] != self.slotid):
status = " "
continue
self.returncode = ''
#
self.flags = v[2]
#
if (self.flags.count('TOKEN_PRESENT') !=0):
#
tokinf = v[3]
sn = tokinf[3].strip()
if (self.sn != sn):
status = ' =\"' + sn + '\" \"' + self.sn + '\"'
dd = ''
return (dd, status)
status = ''
break
else:
dd = ''
status = " "
return (dd, status)
tt = tokinf
dd = dict(Label=tt[0].strip())
dd.update(Manufacturer=tt[1].strip())
dd.update(Model=tt[2].strip())
dd.update(SerialNumber=tt[3].strip())
self.infotok = dd
#
return (dd, status)
A full description of the Token class can be found here.
import sys
import pyp11
class Token:
def __init__ (self, handlelp11, slottoken, serialnum):
flags = ''
self.pyver = sys.version[0]
if (self.pyver == '2'):
print (' python3')
quit()
# handle PKCS#11
self.handle = handlelp11
#
self.slotid = slottoken
#
self.sn = serialnum
#
ret, stat = self.tokinfo()
#
if (stat != ''):
#
self.returncode = stat
return
# ()
def tokinfo(self):
status = ''
#
try:
slots = pyp11.listslots(self.handle)
except:
#
e = sys.exc_info()[1]
e1 = e.args[0]
dd = ''
status = e1
return (dd, status)
status = ''
#
#
for v in slots:
#
if (v[0] != self.slotid):
status = " "
continue
self.returncode = ''
#
self.flags = v[2]
#
if (self.flags.count('TOKEN_PRESENT') !=0):
#
tokinf = v[3]
sn = tokinf[3].strip()
if (self.sn != sn):
status = ' =\"' + sn + '\" \"' + self.sn + '\"'
dd = ''
return (dd, status)
status = ''
break
else:
dd = ''
status = " "
return (dd, status)
tt = tokinf
dd = dict(Label=tt[0].strip())
dd.update(Manufacturer=tt[1].strip())
dd.update(Model=tt[2].strip())
dd.update(SerialNumber=tt[3].strip())
self.infotok = dd
return (dd, status)
def listcerts(self):
try:
status = ''
lcerts = pyp11.listcerts(self.handle, self.slotid)
except:
#
e = sys.exc_info()[1]
e1 = e.args[0]
lcerts = ''
status = e1
return (lcerts, status)
def listobjects(self, type1, value = '' ):
try:
status = ''
if (value == ''):
lobjs = pyp11.listobjects(self.handle, self.slotid, type1)
else:
lobjs = pyp11.listobjects(self.handle, self.slotid, type1, value)
except:
#
e = sys.exc_info()[1]
e1 = e.args[0]
lobjs = ''
status = e1
return (lobjs, status)
def rename(self, type, pkcs11id, label):
try:
status = ''
dd = dict(pkcs11_id=pkcs11id, pkcs11_label=label)
ret = pyp11.rename(self.handle, self.slotid, type, dd)
except:
#
e = sys.exc_info()[1]
e1 = e.args[0]
ret = ''
status = e1
return (ret, status)
def changeckaid(self, type, pkcs11id, pkcs11idnew):
try:
status = ''
dd = dict(pkcs11_id=pkcs11id, pkcs11_id_new=pkcs11idnew)
ret = pyp11.rename(self.handle, self.slotid, type, dd)
except:
#
e = sys.exc_info()[1]
e1 = e.args[0]
ret = ''
status = e1
return (ret, status)
def login(self, userpin):
try:
status = ''
bb = pyp11.login (self.handle, self.slotid, userpin)
except:
e = sys.exc_info()[1]
e1 = e.args[0]
bb = 0
status = e1
return (bb, status)
def logout(self):
try:
status = ''
bb = pyp11.logout (self.handle, self.slotid)
except:
e = sys.exc_info()[1]
e1 = e.args[0]
bb = 0
status = e1
return (bb, status)
def keypair(self, typek, paramk, labkey):
#
gost2012_512 = ['1.2.643.7.1.2.1.2.1', '1.2.643.7.1.2.1.2.2', '1.2.643.7.1.2.1.2.3']
gost2012_256 = ['1.2.643.2.2.35.1', '1.2.643.2.2.35.2', '1.2.643.2.2.35.3', '1.2.643.2.2.36.0', '1.2.643.2.2.36.1', '1.2.643.7.1.2.1.1.1', '1.2.643.7.1.2.1.1.2', '1.2.643.7.1.2.1.1.3', '1.2.643.7.1.2.1.1.4']
gost2001 = ['1.2.643.2.2.35.1', '1.2.643.2.2.35.2', '1.2.643.2.2.35.3', '1.2.643.2.2.36.0', '1.2.643.2.2.36.1']
#
typekey = ['g12_256', 'g12_512', 'gost2001']
genkey = ''
if (typek == typekey[0]):
gost = gost2012_256
elif (typek == typekey[1]):
gost = gost2012_512
elif (typek == typekey[2]):
gost = gost2001
else:
status = ' '
return (genkey, status)
if (gost.count(paramk) == 0) :
status = ' '
return (genkey, status)
try:
# ,
status = ''
genkey = pyp11.keypair(self.handle, self.slotid, typek, paramk, labkey)
except:
#
e = sys.exc_info()[1]
e1 = e.args[0]
print (e1)
#
status = e1
return (genkey, status)
def digest(self, typehash, source):
#
try:
status = ''
digest_hex = pyp11.digest (self.handle, self.slotid, typehash, source)
except:
e = sys.exc_info()[1]
e1 = e.args[0]
#
status = e1
digest_hex = ''
return (digest_hex, status)
#
def sign(self, ckmpair, digest_hex, idorhandle):
# CKA_ID handle
try:
status = ''
sign_hex = pyp11.sign(self.handle, self.slotid, ckmpair, digest_hex, idorhandle)
except:
e = sys.exc_info()[1]
e1 = e.args[0]
#
status = e1
sign_hex = ''
return (sign_hex, status)
#
def verify(self, digest_hex, sign_hex, pubkeyinfo):
# CKA_ID handle
try:
status = ''
verify = pyp11.verify(self.handle, self.slotid, digest_hex, sign_hex, pubkeyinfo)
except:
e = sys.exc_info()[1]
e1 = e.args[0]
# status
verify = 0
status = e1
return (verify, status)
#
def inittoken(self, sopin, labtoken):
try:
status = ''
dd = pyp11.inittoken (self.handle, self.slotid, sopin, labtoken)
except:
e = sys.exc_info()[1]
e1 = e.args[0]
# status
dd = 0
status = e1
return (dd, status)
# PIN-
def inituserpin(self, sopin, userpin):
try:
status = ''
dd = pyp11.inituserpin (self.handle, self.slotid, sopin, userpin)
except:
e = sys.exc_info()[1]
e1 = e.args[0]
# status
dd = 0
status = e1
return (dd, status)
# PIN-
def changeuserpin(self, oldpin, newpin):
try:
status = ''
dd = pyp11.setpin (self.handle, self.slotid, 'user', oldpin, newpin)
except:
e = sys.exc_info()[1]
e1 = e.args[0]
# status
dd = 0
status = e1
self.closesession ()
return (dd, status)
def closesession(self):
try:
status = ''
dd = pyp11.closesession (self.handle)
except:
e = sys.exc_info()[1]
e1 = e.args[0]
# status
dd = 0
status = e1
return (dd, status)
def parsecert(self, cert_der_hex):
try:
status = ''
dd = pyp11.parsecert (self.handle, self.slotid, cert_der_hex)
except:
#
e = sys.exc_info()[1]
e1 = e.args[0]
# status
dd = ''
status = e1
return (dd, status)
def importcert(self, cert_der_hex, labcert):
try:
status = ''
dd = pyp11.importcert (self.handle, self.slotid, cert_der_hex, labcert)
except:
e = sys.exc_info()[1]
e1 = e.args[0]
# status
dd = ''
status = e1
return (dd, status)
def delobject(self, hobject):
try:
status = ''
hobjc = dict(hobj=hobject)
dd = pyp11.delete(self.handle, self.slotid, 'obj', hobjc)
except:
e = sys.exc_info()[1]
e1 = e.args[0]
# status
dd = ''
status = e1
return (dd, status)
def delete(self, type, pkcs11id):
if (type == 'obj'):
dd = ''
status = 'delete for type obj use nethod delobject'
return (dd, status)
try:
status = ''
idobj = dict(pkcs11_id=pkcs11id)
dd = pyp11.delete(self.handle, self.slotid, type, idobj)
except:
e = sys.exc_info()[1]
e1 = e.args[0]
# status
dd = ''
status = e1
return (dd, status)
def listmechs(self):
try:
status = ''
dd = pyp11.listmechs (self.handle, self.slotid)
except:
#
e = sys.exc_info()[1]
e1 = e.args[0]
# status
dd = ''
status = e1
return (dd, status)
Let's consider using the functionality of the pyp11 module and similar operators using the Token class.
In the latter case, it will be necessary to create the token object as well:
< > = Token(< >, < >, < >)
if (< >.returncode != ''):
print(' :')
#
print(< >.returncode)
#
del < >
quit()
Let's start by initializing the token:
try:
ret = pyp11.inittoken (< >, < >, <SO-PIN>, < >)
except:
#
e = sys.exc_info()[1]
e1 = e.args[0]
print (e1)
quit()
The similar code when using the Token class looks like this (object identifier t1):
ret, stat = t1.inittoken(<SO-PIN>, < >)
#
if (stat != ''):
print(' :')
#
print(stat)
quit()
Next, we just give the correspondence between the main operators of the pyp11 module and methods of the Token class without handling exceptions and errors:
<handle> := < pkcs11> <slot> := < > <error> := < > <ret> := < > <cert_der_hex> := < DER- HEX-> ================================================= # PIN- <ret> = pyp11.inituserpin (<handle>, <slot>, <SO-PIN>, <USER-PIN>) <ret>, <error> = < >.inituserpin (<SO-PIN>, <USER-PIN>) # USER-PIN <ret> = pyp11.setpin (<handle>, <slot>, 'user', <USER-PIN >, <USER-PIN >) <ret>, <error> = t1.changeuserpin (<USER-PIN >, <USER-PIN >) # SO-PIN <ret> = pyp11.setpin (<handle>, <slot>, 'so', <SO-PIN >, <SO-PIN >) <ret>, <error> = t1.changesopin (<SO-PIN >, <SO-PIN >) #Login <ret> = pyp11.login (<handle>, <slot>, <USER-PIN>) <ret>, <error> = t1.login (<USER-PIN>) #Logout <ret> = pyp11.logout (<handle>, <slot>) <ret>, <error> = t1.logout () # <ret> = pyp11.closesession (<handle>) <ret>, <error> = t1.closesession () # <ret> = pyp11.listcerts (<handle>, <slot>) <ret>, <error> = t1.listcerts () # <ret> = pyp11.listobjects (<handle>, <slot>, <'cert' | 'pubkey' | 'privkey' | 'data' | 'all'> [, 'value']) <ret>, <error> = t1.listobjects (<'cert' | 'pubkey' | 'privkey' | 'data' | 'all'> [, 'value']) # <ret> = pyp11.parsecert (<handle>, <slot>, <cert_der_hex>) <ret>, <error> = t1.parsecert(<cert_der_hex>) # <ret> = pyp11.importcert (<handle>, <slot>, <cert_der_hex>, < >) <ret>, <error> = t1.importcert(<cert_der_hex>, < >) # <ret> = pyp11.digest (<handle>, <slot>, < >, <>) <ret>, <error> = t1.digest(< >, <>) # <ret> = pyp11.digest (<handle>, <slot>, < >, < >, <CKA_ID | handle >) <ret>, <error> = t1.digest(< >, < >, <CKA_ID | handle >) # <ret> = pyp11.verify (<handle>, <slot>, < >, <>, <asn1- subjectpublickeyinfo hex>) <ret>, <error> = t1.verify(< >, <>, <asn1- subjectpublickeyinfo hex>) # <ret> = pyp11.keypair (<handle>, <slot>, < >, <OID >, <CKA_LABEL>) <ret>, <error> = t1.keypair(< >, <OID >, <CKA_LABEL>)
III. Building and installing pyp11 module with Token class
Building and installing the pyp11 module with the Token class is no different from the one described in the first part.
So, download the archive and unpack it. We go into the PythonPKCS11 folder and execute the installation command:
python3 setup.py install
After installing the module, go to the tests folder and run tests for the pyp11 module.
To test the Token class, go to the test / classtoken folder.
To connect the pyp11 module and the Token class to the scripts, just add the following operators:
import pyp11
from Token import Token
IV. Conclusion
In the near future, the third part of the article should appear, which will tell you how to add support for Russian cryptography to the PyKCS11 project .
PS I want to say thank you svyatikov for helping to test the project on the Windows platform.