mirror of
https://github.com/LedgerHQ/openpgp-card-app
synced 2024-11-09 07:10:30 +00:00
cf6e295e47
Fix PIN management PINPAD CCID command was in conflict with new PIN get status APDU. Use class CLA=0xEF to differenciate both case. Fix Terminate status management Fix RC code setting Add MSE support Set MSE supported in capabilities
746 lines
23 KiB
Python
746 lines
23 KiB
Python
# Copyright 2017 Cedric Mesnil <cslashm@gmail.com>, Ledger SAS
|
|
#
|
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
# you may not use this file except in compliance with the License.
|
|
# You may obtain a copy of the License at
|
|
#
|
|
# http://www.apache.org/licenses/LICENSE-2.0
|
|
#
|
|
# Unless required by applicable law or agreed to in writing, software
|
|
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
# See the License for the specific language governing permissions and
|
|
# limitations under the License.
|
|
#
|
|
|
|
|
|
try:
|
|
from ledgerblue.comm import getDongle
|
|
from ledgerblue.commException import CommException
|
|
except :
|
|
pass
|
|
import binascii
|
|
|
|
from smartcard.System import readers
|
|
import sys
|
|
import datetime
|
|
import pickle
|
|
|
|
# decode level 0 of tlv|tlv|tlv
|
|
# return dico {t: v, t:v, ...}
|
|
#tlv: hexstring
|
|
|
|
def decode_tlv(tlv) :
|
|
tags = {}
|
|
while len(tlv) :
|
|
o = 0
|
|
l = 0
|
|
if (tlv[0] & 0x1F) == 0x1F:
|
|
t = (tlv[0]<<8)|tlv[1]
|
|
o = 2
|
|
else:
|
|
t = tlv[0]
|
|
o = 1
|
|
l = tlv[o]
|
|
if l & 0x80 :
|
|
if (l&0x7f) == 1:
|
|
l = tlv[o+1]
|
|
o += 2
|
|
if (l&0x7f) == 2:
|
|
l = (tlv[o+1]<<8)|tlv[o+2]
|
|
o += 3
|
|
else:
|
|
o += 1
|
|
v = tlv[o:o+l]
|
|
tags[t] = v
|
|
tlv = tlv[o+l:]
|
|
return tags
|
|
|
|
class GPGCard() :
|
|
def __init__(self):
|
|
self.reset()
|
|
|
|
def reset(self):
|
|
#token info
|
|
self.AID = b''
|
|
self.aid = b''
|
|
self.ext_length = b''
|
|
self.ext_capabilities = b''
|
|
self.histo_bytes = b''
|
|
self.PW_status = b''
|
|
|
|
#user info
|
|
self.login = b''
|
|
self.url = b''
|
|
self.name = b''
|
|
self.sex = b''
|
|
self.lang = b''
|
|
|
|
#keys info
|
|
self.cardholder_cert = b''
|
|
self.sig_attribute = b''
|
|
self.dec_attribute = b''
|
|
self.aut_attribute = b''
|
|
self.sig_fingerprints = b''
|
|
self.dec_fingerprints = b''
|
|
self.aut_fingerprints = b''
|
|
self.sig_CA_fingerprints = b''
|
|
self.dec_CA_fingerprints = b''
|
|
self.aut_CA_fingerprints = b''
|
|
self.sig_date = b''
|
|
self.dec_date = b''
|
|
self.aut_date = b''
|
|
self.cert_aut = b''
|
|
self.cert_dec = b''
|
|
self.cert_sig = b''
|
|
self.UIF_SIG = b''
|
|
self.UIF_DEC = b''
|
|
self.UIF_AUT = b''
|
|
self.digital_counter = b''
|
|
|
|
#private info
|
|
self.private_01 = b''
|
|
self.private_02 = b''
|
|
self.private_03 = b''
|
|
|
|
|
|
|
|
def connect(self, device):
|
|
if device.startswith("ledger:"):
|
|
self.token = getDongle(True)
|
|
self.exchange = self._exchange_ledger
|
|
self.disconnect = self._disconnect_ledger
|
|
elif device.startswith("pcsc:"):
|
|
allreaders = readers()
|
|
for r in allreaders:
|
|
rname = str(r)
|
|
#print('try: %s : %s'%(rname,device[5:]))
|
|
if rname.startswith(device[5:]):
|
|
r.createConnection()
|
|
self.token = r
|
|
self.connection = r.createConnection()
|
|
self.connection.connect()
|
|
self.exchange = self._exchange_pcsc
|
|
self.disconnect = self._disconnect_pcsc
|
|
else:
|
|
#print("No")
|
|
pass
|
|
if not self.token:
|
|
print("No token")
|
|
|
|
|
|
|
|
|
|
### APDU interface ###
|
|
def _exchange_ledger(self,cmd,sw=0x9000):
|
|
resp = b''
|
|
cond = True
|
|
while cond:
|
|
try:
|
|
resp = resp + self.token.exchange(cmd,300)
|
|
sw = 0x9000
|
|
cond = False
|
|
except CommException as e:
|
|
if (e.data) :
|
|
resp = resp + e.data
|
|
sw = e.sw
|
|
if (sw&0xFF00) == 0x6100 :
|
|
cmd = binascii.unhexlify("00C00000%.02x"%(sw&0xFF))
|
|
else:
|
|
cond = False
|
|
return resp,sw
|
|
|
|
def _exchange_pcsc(self,apdu,sw=0x9000):
|
|
#print("xch S cmd : %s"%(binascii.hexlify(apdu)))
|
|
apdu = [x for x in apdu]
|
|
resp, sw1, sw2 = self.connection.transmit(apdu)
|
|
while sw1==0x61:
|
|
apdu = binascii.unhexlify(b"00c00000%.02x"%sw2)
|
|
apdu = [x for x in apdu]
|
|
resp2, sw1, sw2 = self.connection.transmit(apdu)
|
|
resp = resp + resp2
|
|
resp = bytes(resp)
|
|
sw = (sw1<<8)|sw2
|
|
#print("xch S resp: %s %.04x"%(binascii.hexlify(resp),sw))
|
|
return resp,sw
|
|
|
|
def _disconnect_ledger(self):
|
|
return self.token.close()
|
|
|
|
def _disconnect_pcsc(self):
|
|
r = self.connection.disconnect()
|
|
#self.connection.releaseContext()
|
|
return r
|
|
|
|
def select(self):
|
|
apdu = binascii.unhexlify(b"00A4040006D27600012401")
|
|
return self.exchange(apdu)
|
|
|
|
def activate(self):
|
|
apdu = binascii.unhexlify(b"00440000")
|
|
return self.exchange(apdu)
|
|
|
|
def terminate(self):
|
|
apdu = binascii.unhexlify(b"00E60000")
|
|
return self.exchange(apdu)
|
|
|
|
def get_data(self,tag):
|
|
apdu = binascii.unhexlify(b"00CA%.04x00"%tag)
|
|
return self.exchange(apdu)
|
|
|
|
def put_data(self,tag,value):
|
|
apdu = binascii.unhexlify(b"00DA%.04x%.02x"%(tag,len(value)))+value
|
|
return self.exchange(apdu)
|
|
|
|
def verify(self,id,value):
|
|
apdu = binascii.unhexlify(b"002000%.02x%.02x"%(id,len(value)))+value
|
|
return self.exchange(apdu)
|
|
|
|
def change_reference_data(self,id,value,new_value):
|
|
lc = len(value)+len(new_value)
|
|
apdu = binascii.unhexlify(b"002400%.02x%.02x"%(id,lc))+value+new_value
|
|
return self.exchange(apdu)
|
|
|
|
def reset_retry_counter(self,RC,new_value):
|
|
if len(RC)==0:
|
|
p1 = 2
|
|
else:
|
|
p1 = 0
|
|
lc = len(RC)+len(new_value)
|
|
apdu = binascii.unhexlify(b"002C%02x81%.02x"%(p1,lc))+RC+new_value
|
|
return self.exchange(apdu)
|
|
|
|
def generate_asym_key_pair(self, mode, key):
|
|
apdu = binascii.unhexlify(b"0047%02x0002%.04x"%(mode,key))
|
|
return self.exchange(apdu)
|
|
|
|
### API interfaces ###
|
|
def get_all(self):
|
|
self.reset()
|
|
|
|
self.AID,sw = self.get_data(0x4f)
|
|
self.login ,sw = self.get_data(0x5e)
|
|
self.url,sw = self.get_data(0x5f50)
|
|
self.histo_bytes,sw = self.get_data(0x5f52)
|
|
|
|
cardholder,sw = self.get_data(0x65)
|
|
tags = decode_tlv(cardholder)
|
|
if 0x5b in tags:
|
|
self.name = tags[0x5b]
|
|
if 0x5f35 in tags:
|
|
self.sex = tags[0x5f35]
|
|
if 0x5f35 in tags:
|
|
self.lang = tags[0x5f2d]
|
|
|
|
application_data,sw = self.get_data(0x6E)
|
|
tags = decode_tlv(application_data)
|
|
if 0x7f66 in tags:
|
|
self.ext_length = tags[0x7f66]
|
|
if 0x73 in tags:
|
|
dicretionary_data = tags[0x73]
|
|
tags = decode_tlv(dicretionary_data)
|
|
if 0xc0 in tags:
|
|
self.ext_capabilities = tags[0xC0]
|
|
|
|
if 0xc4 in tags:
|
|
self.PW_status = tags[0xC4]
|
|
|
|
if 0xC1 in tags:
|
|
self.sig_attribute = tags[0xC1]
|
|
if 0xC2 in tags:
|
|
self.dec_attribute = tags[0xC2]
|
|
if 0xC3 in tags:
|
|
self.aut_attribute = tags[0xC3]
|
|
if 0xC5 in tags:
|
|
fingerprints = tags[0xC5]
|
|
self.sig_fingerprints = fingerprints[0:20]
|
|
self.dec_fingerprints = fingerprints[20:40]
|
|
self.aut_fingerprints = fingerprints[40:60]
|
|
if 0xC6 in tags:
|
|
fingerprints = tags[0xC6]
|
|
self.sig_CA_fingerprints = fingerprints[0:20]
|
|
self.dec_CA_fingerprints = fingerprints[20:40]
|
|
self.aut_CA_fingerprints = fingerprints[40:60]
|
|
if 0xcd in tags:
|
|
dates = tags[0xCD]
|
|
self.sig_date = dates[0:4]
|
|
self.dec_date = dates[4:8]
|
|
self.aut_date = dates[8:12]
|
|
|
|
self.cardholder_cert = self.get_data(0x7f21)
|
|
|
|
self.UIF_SIG,sw = self.get_data(0xD6)
|
|
self.UIF_DEC,sw = self.get_data(0xD7)
|
|
self.UIF_AUT,sw = self.get_data(0xD8)
|
|
|
|
sec_template,sw = self.get_data(0x7A)
|
|
tags = decode_tlv(sec_template)
|
|
if 0x93 in tags:
|
|
self.digital_counter = tags[0x93]
|
|
|
|
self.private_01,sw = self.get_data(0x0101)
|
|
self.private_02,sw = self.get_data(0x0102)
|
|
self.private_03,sw = self.get_data(0x0103)
|
|
self.private_04,sw = self.get_data(0x0104)
|
|
|
|
def set_all(self):
|
|
self.put_data(0x4f, self.AID[10:14])
|
|
self.put_data(0x0101, self.private_01)
|
|
self.put_data(0x0102, self.private_02)
|
|
self.put_data(0x0103, self.private_03)
|
|
self.put_data(0x0104, self.private_04)
|
|
|
|
self.put_data(0x5b, self.name)
|
|
self.put_data(0x5e, self.login)
|
|
self.put_data(0x5f2d, self.lang)
|
|
self.put_data(0x5f35, self.sex)
|
|
self.put_data(0x5f50, self.url)
|
|
|
|
self.put_data(0xc1, self.sig_attribute)
|
|
self.put_data(0xc2, self.dec_attribute)
|
|
self.put_data(0xc3, self.aut_attribute)
|
|
|
|
self.put_data(0xc4, self.PW_status)
|
|
|
|
self.put_data(0xc7, self.sig_fingerprints)
|
|
self.put_data(0xc8, self.dec_fingerprints)
|
|
self.put_data(0xc9, self.aut_fingerprints)
|
|
self.put_data(0xca, self.sig_CA_fingerprints)
|
|
self.put_data(0xcb, self.dec_CA_fingerprints)
|
|
self.put_data(0xcc, self.aut_CA_fingerprints)
|
|
self.put_data(0xce, self.sig_date)
|
|
self.put_data(0xcf, self.dec_date)
|
|
self.put_data(0xd0, self.aut_date)
|
|
#self.put_data(0x7f21, self.cardholder_cert)
|
|
|
|
self.put_data(0xd6, self.UIF_SIG)
|
|
self.put_data(0xd7, self.UIF_DEC)
|
|
self.put_data(0xd8, self.UIF_AUT)
|
|
|
|
def backup(self, file_name):
|
|
f = open(file_name,mode='w+b')
|
|
self.get_all();
|
|
pickle.dump(
|
|
(self.AID,
|
|
self.private_01, self.private_02, self.private_03, self.private_04,
|
|
self.name, self.login, self.sex, self.url,
|
|
self.sig_attribute, self.dec_attribute, self.aut_attribute,
|
|
self.PW_status,
|
|
self.sig_fingerprints, self.dec_fingerprints, self.aut_fingerprints,
|
|
self.sig_CA_fingerprints, self.dec_CA_fingerprints, self.aut_CA_fingerprints,
|
|
self.sig_date, self.dec_date, self.aut_date,
|
|
self.cardholder_cert,
|
|
self.UIF_SIG, self.UIF_DEC, self.UIF_AUT),
|
|
f, 2)
|
|
|
|
|
|
def restore(self, file_name, seed_key=False):
|
|
f = open(file_name,mode='r+b')
|
|
(self.AID,
|
|
self.private_01, self.private_02, self.private_03, self.private_04,
|
|
self.name, self.login, self.sex, self.url,
|
|
self.sig_attribute, self.dec_attribute, self.aut_attribute,
|
|
self.status,
|
|
self.sig_fingerprints, self.dec_fingerprints, self.aut_fingerprints,
|
|
self.sig_CA_fingerprints, self.dec_CA_fingerprints, self.aut_CA_fingerprints,
|
|
self.sig_date, self.dec_date, self.aut_date,
|
|
self.cardholder_cert,
|
|
self.UIF_SIG, self.UIF_DEC, self.UIF_AUT) = pickle.load(f)
|
|
self.set_all()
|
|
if seed_key :
|
|
apdu = binascii.unhexlify(b"0047800102B600")
|
|
self.exchange(apdu)
|
|
apdu = binascii.unhexlify(b"0047800102B800")
|
|
self.exchange(apdu)
|
|
apdu = binascii.unhexlify(b"0047800102A400")
|
|
self.exchange(apdu)
|
|
|
|
|
|
|
|
|
|
def decode_AID(self):
|
|
return {
|
|
'AID': ('AID' , "%x"%int.from_bytes(self.AID,'big')),
|
|
'RID': ('RID' , "%x"%int.from_bytes(self.AID[0:5],'big')),
|
|
'APP': ('application' , "%.02x"%self.AID[5]),
|
|
'VER': ('version' , "%.02x.%.02x"%(self.AID[6], self.AID[7])),
|
|
'MAN': ('manufacturer' , "%x"%int.from_bytes(self.AID[8:10],'big')),
|
|
'SER': ('serial' , "%x"%int.from_bytes(self.AID[10:14],'big'))
|
|
}
|
|
|
|
def decode_histo(self):
|
|
return {
|
|
'HIST': ('historical bytes', binascii.hexlify(self.histo_bytes))
|
|
}
|
|
|
|
def decode_extlength(self):
|
|
if self.ext_length:
|
|
return {
|
|
'CMD': ('Max command length' , "%d" %((self.ext_length[2]<<8)|self.ext_length[3])),
|
|
'RESP':( 'Max response length' , "%d" %((self.ext_length[6]<<8)|self.ext_length[7]))
|
|
}
|
|
else:
|
|
return {
|
|
'CMD': ('Max command length' , "unspecified"),
|
|
'RESP':( 'Max response length' ,"unspecified"),
|
|
}
|
|
|
|
def decode_capabilities(self):
|
|
d = {}
|
|
b1 = self.ext_capabilities[0]
|
|
if b1&0x80 :
|
|
if self.ext_capabilities[1] == 1:
|
|
d['SM'] = ('Secure Messaging', "yes: 128 bits")
|
|
elif self.ext_capabilities[1] == 2:
|
|
d['SM'] = ('Secure Messaging', "yes: 256 bits")
|
|
else:
|
|
d['SM'] = ('Secure Messaging', "yes: ?? bits")
|
|
else:
|
|
d['SM'] = ('Secure Messaging', "no")
|
|
|
|
if b1&0x40 :
|
|
d['CHAL'] = ('GET CHALLENGE', "yes")
|
|
else:
|
|
d['CHAL'] = ('GET CHALLENGE', "no")
|
|
|
|
if b1&0x20 :
|
|
d['KEY'] = ('Key import', "yes")
|
|
else:
|
|
d['KEY'] = ('Key import', "no")
|
|
|
|
if b1&0x10 :
|
|
d['PWS'] = ('PW status changeable', "yes")
|
|
else:
|
|
d['PWS'] = ('PW status changeable', "no")
|
|
|
|
if b1&0x08 :
|
|
d['PDO'] = ('Private DOs', "yes")
|
|
else:
|
|
d['PDO'] = ('Private DOs', "no")
|
|
|
|
if b1&0x04 :
|
|
d['ATTR'] = ('Algo attributes changeable', "yes")
|
|
else:
|
|
dd['ATTR'] = ('Algo attributes changeable', "no")
|
|
|
|
if b1&0x02 :
|
|
d['PSO'] = ('PSO:DEC support AES', "yes")
|
|
else:
|
|
d['PSO'] = ('PSO:DEC support AES', "no")
|
|
|
|
|
|
d['CHAL_MAX'] = ('Max GET_CHALLENGE length',
|
|
"%d"% ((self.ext_capabilities[2]<<8)|self.ext_capabilities[3]))
|
|
d['CERT_MAX'] = ('Max Cert length',
|
|
"%d"% ((self.ext_capabilities[4]<<8)|self.ext_capabilities[5]))
|
|
d['PDO_MAX'] = ('Max special DO length',
|
|
"%d"% ((self.ext_capabilities[6]<<8)|self.ext_capabilities[7]))
|
|
if self.ext_capabilities[8] :
|
|
d['PIN2'] = ('PIN 2 format supported', "yes")
|
|
else:
|
|
d['PIN2'] = ('PIN 2 format supported',"no")
|
|
|
|
return d
|
|
|
|
def decode_pws(self):
|
|
d = {}
|
|
if self.PW_status[0]==0:
|
|
d['ONCE'] = ('PW1 valid for several CDS', 'yes')
|
|
elif self.PW_status[0]==1:
|
|
d['ONCE'] = ('PW1 valid for several CDS', 'no')
|
|
else:
|
|
d['ONCE'] = ('PW1 valid for several CDS', 'unknown (%d)'%self.PW_status[0])
|
|
|
|
if self.PW_status[1] & 0x80:
|
|
fmt = "Format-2"
|
|
else:
|
|
fmt = "UTF-8"
|
|
pwlen = self.PW_status[1] & 0x7f
|
|
d['PW1'] = ("PW1 format", "%s : %d bytes"%(fmt,pwlen))
|
|
|
|
if self.PW_status[2] & 0x80:
|
|
fmt = "Format-2"
|
|
else:
|
|
fmt = "UTF-8"
|
|
pwlen = self.PW_status[2] & 0x7f
|
|
d['RC'] = ("RC format", "%s : %d bytes"%(fmt,pwlen))
|
|
|
|
if self.PW_status[3] & 0x80:
|
|
fmt = "Format-2"
|
|
else:
|
|
fmt = "UTF-8"
|
|
pwlen = self.PW_status[3] & 0x7f
|
|
d['PW3'] = ("PW3 format", "%s : %d bytes"%(fmt,pwlen))
|
|
|
|
d['CNT1'] = ('PW1 counter', "%x"%self.PW_status[4])
|
|
d['CNTRC'] =('RC counter', "%x"%self.PW_status[5])
|
|
d['CNT3'] = ('PW3 counter', "%x"%self.PW_status[6])
|
|
|
|
return d
|
|
|
|
#USER Info
|
|
# internals are always store as byres, get/set automatically convert from/to
|
|
def set_name(self,name):
|
|
""" Args:
|
|
name (str) : utf8 string
|
|
"""
|
|
self.name = name.encode('utf-8')
|
|
self.put_data( 0x5b, self.name)
|
|
|
|
def get_name(self):
|
|
return self.name.decode('utf-8')
|
|
|
|
def set_login(self,login):
|
|
""" Args:
|
|
login (str) : utf8 string
|
|
"""
|
|
self.login = login.encode('utf-8')
|
|
self.put_data( 0x5e, self.login)
|
|
|
|
def get_login(self):
|
|
return self.login.decode('utf-8')
|
|
|
|
def set_url(self,url):
|
|
""" Args:
|
|
url (str) : utf8 string
|
|
"""
|
|
self.url = url.encode('utf-8')
|
|
self.put_data(0x5f50, self.url)
|
|
|
|
def get_url(self):
|
|
return self.url.decode('utf-8')
|
|
|
|
def set_sex(self,sex):
|
|
""" Args:
|
|
sex (str) : ascii string ('9', '1', '2')
|
|
"""
|
|
self.sex = sex.encode('utf-8')
|
|
self.put_data(0x5f35, self.sex)
|
|
|
|
def get_sex(self):
|
|
return self.sex.decode('utf-8')
|
|
|
|
def set_lang(self,lang):
|
|
""" Args:
|
|
lang (str) : utf8 string
|
|
"""
|
|
self.lang = lang.encode('utf-8')
|
|
self.put_data(0x5f2d, self.lang)
|
|
|
|
def get_lang(self):
|
|
return self.lang.decode('utf-8')
|
|
|
|
|
|
#PINs
|
|
def verify_pin(self,id,value):
|
|
""" Args:
|
|
id (int) : 0x81, 0x82, ox83
|
|
value (str) : ascii string
|
|
"""
|
|
value = value.encode('ascii')
|
|
resp,sw = self.verify(id,value)
|
|
return sw == 0x9000
|
|
|
|
def change_pin(self, id, value,new_value):
|
|
""" Args:
|
|
id (int) : 0x81, ox83
|
|
value (str) : ascii string
|
|
"""
|
|
value = value.encode('ascii')
|
|
new_value = new_value.encode('ascii')
|
|
resp,sw = self.change_reference_data(id,value,new_value)
|
|
return sw == 0x9000
|
|
|
|
def change_RC(self,new_value):
|
|
""" Args:
|
|
id (int) : 0x81, ox83
|
|
value (str) : ascii string
|
|
"""
|
|
new_value = new_value.encode('ascii')
|
|
resp,sw = self.put_data(0xd3,new_value)
|
|
return sw == 0x9000
|
|
|
|
def reset_PW1(self,RC,new_value):
|
|
""" Args:
|
|
id (int) : 0x81, ox83
|
|
value (str) : ascii string
|
|
"""
|
|
new_value = new_value.encode('ascii')
|
|
RC = RC.encode('ascii')
|
|
resp,sw = self.reset_retry_counter(RC,new_value)
|
|
return sw == 0x9000
|
|
|
|
#keys
|
|
def get_key_uif(self,key):
|
|
"""
|
|
Returns: (int) 0,1,2,256(not supported)
|
|
"""
|
|
uif = None
|
|
if key=='sig':
|
|
uif = self.UIF_SIG
|
|
if key=='aut':
|
|
uif = self.UIF_DEC
|
|
if key=='dec':
|
|
uif = self.UIF_AUT
|
|
|
|
if uif:
|
|
uif = int.from_bytes(uif,'big')
|
|
else:
|
|
uif = 256
|
|
return uif
|
|
|
|
def get_key_fingerprints(self, key):
|
|
"""
|
|
Returns: (str) fingerprints hex string
|
|
"""
|
|
fprints = None
|
|
if key=='sig':
|
|
fprints = self.sig_fingerprints
|
|
if key=='aut':
|
|
fprints = self.aut_fingerprints
|
|
if key=='dec':
|
|
fprints = self.dec_fingerprints
|
|
if fprints:
|
|
fprints = binascii.hexlify(fprints)
|
|
else:
|
|
fprint = '-'
|
|
return fprints.decode('ascii')
|
|
|
|
def get_key_CA_fingerprints(self, key):
|
|
"""
|
|
Returns: (str) CA fingerprints hex string
|
|
"""
|
|
fprints = None
|
|
if key=='sig':
|
|
fprints = self.sig_CA_fingerprints
|
|
if key=='aut':
|
|
fprints = self.aut_CA_fingerprints
|
|
if key=='dec':
|
|
fprints = self.dec_CA_fingerprints
|
|
if fprints:
|
|
fprints = binascii.hexlify(fprints)
|
|
else:
|
|
fprint = b'-'
|
|
return fprints.decode('ascii')
|
|
|
|
def get_key_date(self, key):
|
|
"""
|
|
Returns: (str) date
|
|
"""
|
|
fdate = None
|
|
if key=='sig':
|
|
fdate = self.sig_date
|
|
if key=='aut':
|
|
fdate = self.aut_date
|
|
if key=='dec':
|
|
fdate = self.dec_date
|
|
if fdate:
|
|
fdate = datetime.datetime.fromtimestamp(int.from_bytes(fdate,'big')).isoformat(' ')
|
|
else:
|
|
fprint = b'-'.decode('ascii')
|
|
return fdate
|
|
|
|
|
|
def get_key_attribute(self, key):
|
|
"""
|
|
for RSA:
|
|
{'id': (int) 0x01,
|
|
'nsize': (int)
|
|
'esize' (int)
|
|
'format': (int)
|
|
}
|
|
|
|
for ECC:
|
|
|
|
{'id': (int) 0x18|0x19,
|
|
'OID': (bytes)
|
|
}
|
|
|
|
Args:
|
|
key: (str) 'sig' | 'aut', 'dec'
|
|
"""
|
|
|
|
attributes = None
|
|
if key=='sig':
|
|
attributes = self.sig_attribute
|
|
if key=='dec':
|
|
attributes = self.dec_attribute
|
|
if key=='aut':
|
|
attributes = self.aut_attribute
|
|
if not attributes:
|
|
return None
|
|
if len(attributes) == 0:
|
|
return None
|
|
|
|
if attributes[0] == 0x01:
|
|
return {
|
|
'id': 1,
|
|
'nsize': (attributes[1]<<8) | attributes[2],
|
|
'esize': (attributes[3]<<8) | attributes[4],
|
|
'format': attributes[5]
|
|
}
|
|
if attributes[0] == 18 or attributes[0] == 19 :
|
|
return {
|
|
'id': attributes[0] ,
|
|
'oid': attributes[1:]
|
|
}
|
|
print ("NONE: %s"%binascii.hexlify(attributes))
|
|
return None
|
|
|
|
def set_template(self, template):
|
|
"""
|
|
See get_template
|
|
"""
|
|
pass
|
|
|
|
|
|
def asymmetric_key(self, op, key) :
|
|
"""
|
|
Args:
|
|
op: (int) 0x80 generate, 0x81 read pub, 0x82 read pub&priv
|
|
key: (str) 'sig' | 'aut', 'dec'
|
|
|
|
Returns:
|
|
for RSA:
|
|
{'id': (int) 0x01,
|
|
'n': (bytes)
|
|
'e' (bytes)
|
|
'd': (bytes)
|
|
}
|
|
|
|
for ECC:
|
|
|
|
{'id': (int) 0x18|0x19,
|
|
'OID': (bytes)
|
|
}
|
|
|
|
"""
|
|
attributes = None
|
|
if key=='sig':
|
|
attributes = self.sig_attribute
|
|
key = 0xb600
|
|
if key=='dec':
|
|
attributes = self.dec_attribute
|
|
key = 0xb800
|
|
if key=='aut':
|
|
attributes = self.aut_attribute
|
|
key = 0xa400
|
|
if not attributes:
|
|
return None
|
|
if len(attributes) == 0:
|
|
return None
|
|
resp,sw = self.generate_asym_key_pair(op,key)
|
|
if sw != 0x9000:
|
|
return None
|
|
resp,sw = self.generate_asym_key_pair(0x82,key)
|
|
tags = decode_tlv(resp)
|
|
tags = decode_tlv(tags[0x7f49])
|
|
if attributes[0] == 0x01:
|
|
return {
|
|
'id': 1,
|
|
'n': tags[0x81],
|
|
'e': tags[0x82],
|
|
'd': tags[0x98],
|
|
}
|
|
|