-
Notifications
You must be signed in to change notification settings - Fork 204
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
WIP: Support for Portuguese Electronic ID Cards #208
base: master
Are you sure you want to change the base?
Changes from 5 commits
673c3d5
b7ae69f
f82bda3
419b3cd
78a1e30
3ccab73
b98566b
0065c16
2424d74
8d09ad9
bd2cb22
e5124d4
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
# Portuguese Electronic Identity Cards | ||
|
||
Basic support for PTEID cards is added. | ||
The cards cannot be used with the standard PKI and tools as the objects are signed by a self generated CA. | ||
The Object structure, permissions and certificates tries to follow the same structure as original cards. | ||
|
||
This is useful for development purposes, especially when using the PKCS11 interface. | ||
Some objects may be inconsistent as they were copied from a development card. Further developments will focus on properly emulating PTEID cards. | ||
|
Large diffs are not rendered by default.
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -225,7 +225,6 @@ def make_property(prop, doc): | |
lambda self: delattr(self, "_"+prop), | ||
doc) | ||
|
||
|
||
class File(object): | ||
"""Template class for a smartcard file.""" | ||
bertlv_data = make_property("bertlv_data", "list of (tag, length, " | ||
|
@@ -412,19 +411,20 @@ class DF(File): | |
"""Class for a dedicated file""" | ||
data = make_property("data", "unknown") | ||
content = make_property("content", "list of files of the DF") | ||
dfname = make_property("dfname", "string with up to 16 bytes. DF name," | ||
"which can also be used as application" | ||
"identifier.") | ||
dfname = b"" # make_property("dfname", "string with up to 16 bytes. DF name," | ||
# "which can also be used as application" | ||
# "identifier.") | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. why comment this out? |
||
|
||
def __init__(self, parent, fid, | ||
filedescriptor=FDB["NOTSHAREABLEFILE"] | FDB["DF"], | ||
lifecycle=LCB["ACTIVATED"], | ||
simpletlv_data=None, bertlv_data=None, dfname=None, data=""): | ||
simpletlv_data=None, bertlv_data=None, dfname=None, data="", | ||
extra_fci_data=b''): | ||
""" | ||
See File for more. | ||
""" | ||
File.__init__(self, parent, fid, filedescriptor, lifecycle, | ||
simpletlv_data, bertlv_data) | ||
simpletlv_data, bertlv_data, None, extra_fci_data) | ||
if dfname: | ||
if len(dfname) > 16: | ||
raise SwError(SW["ERR_INCORRECTPARAMETERS"]) | ||
|
@@ -515,10 +515,10 @@ def select(self, attribute, value, reference=REF["IDENTIFIER_FIRST"], | |
# not found | ||
if isinstance(value, int): | ||
logging.debug("file (%s=%x) not found in:\n%s" % | ||
(attribute, value, self)) | ||
(attribute, value, self.fid)) | ||
elif isinstance(value, str): | ||
logging.debug("file (%s=%r) not found in:\n%s" % | ||
(attribute, value, self)) | ||
(attribute, value, self.fid)) | ||
raise SwError(SW["ERR_FILENOTFOUND"]) | ||
|
||
def remove(self, file): | ||
|
@@ -679,30 +679,43 @@ def _selectFile(self, p1, p2, data): | |
P1_PATH_FROM_MF = 0x08 | ||
P1_PATH_FROM_CURRENTDF = 0x09 | ||
|
||
if (p1 >> 4) != 0 or p1 == P1_FILE: | ||
#if (p1 >> 4) != 0 or | ||
if p1 == P1_FILE: | ||
import binascii | ||
logging.debug(f"p1 >> 4 or P1_FILE: {binascii.hexlify(data)}") | ||
# RFU OR | ||
# When P1='00', the card knows either because of a specific coding | ||
# of the file identifier or because of the context of execution of | ||
# the command if the file to select is the MF, a DF or an EF. | ||
if data[:2] == inttostring(self.fid): | ||
selected = walk(self, data[2:]) | ||
elif data[:2] == inttostring(self.currentDF().fid): | ||
selected = walk(self.currentDF(), data[2:]) | ||
else: | ||
selected = walk(self.currentDF(), data) | ||
try: | ||
if data[:2] == inttostring(self.fid): | ||
selected = walk(self, data[2:]) | ||
elif data[:2] == inttostring(self.currentDF().fid): | ||
selected = walk(self.currentDF(), data[2:]) | ||
else: | ||
selected = walk(self.currentDF(), data) | ||
except SwError as e: | ||
# If everything fails, look at MF | ||
selected = walk(self, data) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. technically speaking, this is an error, because the application specifically did not do the selection from MF; typically, there is no fall back directory, just the error is returned... |
||
|
||
elif p1 == P1_CHILD_DF or p1 == P1_CHILD_EF: | ||
logging.debug("P1_CHILD_DF or P1_CHILD_EF") | ||
selected = self.currentDF().select('fid', stringtoint(data)) | ||
if ((p1 == P1_CHILD_DF and not isinstance(selected, DF)) or | ||
(p1 == P1_CHILD_EF and not isinstance(selected, EF))): | ||
# Command incompatible with file structure | ||
raise SwError(SW["ERR_INCOMPATIBLEWITHFILE"]) | ||
elif p1 == P1_PATH_FROM_MF: | ||
logging.debug("P1 PATH FROM MF") | ||
selected = walk(self, data) | ||
elif p1 == P1_PATH_FROM_CURRENTDF: | ||
logging.debug("P1 PATH FROM CURRENT DF") | ||
selected = walk(self.currentDF(), data) | ||
elif p1 == P1_PARENT_DF: | ||
logging.debug("P1 PARENT DF") | ||
selected = self.current.parent | ||
elif p1 == P1_DF_NAME: | ||
logging.debug("P1 DF NAME") | ||
df = self.currentDF() | ||
if df == self or df not in self.content: | ||
index_current = -1 | ||
|
@@ -748,7 +761,6 @@ def selectFile(self, p1, p2, data): | |
data = bertlv_pack([(tag, len(fdm), fdm)]) | ||
|
||
self.current = file | ||
logging.info("Selected %s" % file) | ||
|
||
return SW["NORMAL"], data | ||
|
||
|
@@ -761,8 +773,8 @@ def dataUnitsDecodePlain(self, p1, p2, data): | |
list of data strings. | ||
""" | ||
if p1 >> 7: | ||
# If bit 1 of INS is set to 0 and bit 8 of P1 to 1, then bits 7 | ||
# and 6 of P1 are set to 00 (RFU), bits 5 to 1 of P1 encode a | ||
# If bit 8 of INS is set to 1, then bits 7 | ||
# and 6 of P1 are set to 00 (RFU), bits 3 to 1 of P1 encode a | ||
# short EF identifier and P2 (eight bits) encodes an offset | ||
# from zero to 255. | ||
ef = self.currentDF().select('shortfid', p1 & 0x1f) | ||
|
@@ -817,8 +829,8 @@ def readBinaryPlain(self, p1, p2, data): | |
data as binary string. | ||
""" | ||
ef, offsets, datalist = self.dataUnitsDecodePlain(p1, p2, data) | ||
result = ef.readbinary(offsets[0]) | ||
|
||
result = ef.readbinary(offsets[0]) | ||
return SW["NORMAL"], result | ||
|
||
def readBinaryEncapsulated(self, p1, p2, data): | ||
|
@@ -1423,7 +1435,8 @@ class EF(File): | |
def __init__(self, parent, fid, filedescriptor, | ||
lifecycle=LCB["ACTIVATED"], | ||
simpletlv_data=None, bertlv_data=None, | ||
datacoding=DCB["ONETIMEWRITE"], shortfid=0): | ||
datacoding=DCB["ONETIMEWRITE"], shortfid=0, | ||
extra_fci_data=b''): | ||
""" | ||
The constructor is supposed to be involved creation of a by creation of | ||
a TransparentStructureEF or RecordStructureEF. | ||
|
@@ -1439,7 +1452,7 @@ def __init__(self, parent, fid, filedescriptor, | |
self.shortfid = shortfid | ||
File.__init__(self, parent, fid, filedescriptor, lifecycle, | ||
simpletlv_data, | ||
bertlv_data) | ||
bertlv_data, extra_fci_data=extra_fci_data) | ||
self.datacoding = datacoding | ||
|
||
|
||
|
@@ -1451,14 +1464,15 @@ def __init__(self, parent, fid, | |
filedescriptor=FDB["EFSTRUCTURE_TRANSPARENT"], | ||
lifecycle=LCB["ACTIVATED"], | ||
simpletlv_data=None, bertlv_data=None, | ||
datacoding=DCB["ONETIMEWRITE"], shortfid=0, data=""): | ||
datacoding=DCB["ONETIMEWRITE"], shortfid=0, data="", | ||
extra_fci_data=b''): | ||
""" | ||
See EF for more. | ||
""" | ||
EF.__init__(self, parent, fid, | ||
filedescriptor, lifecycle, | ||
simpletlv_data, bertlv_data, | ||
datacoding, shortfid) | ||
datacoding, shortfid, extra_fci_data) | ||
self.data = data | ||
|
||
def readbinary(self, offset): | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I believe you're using the first byte of a binary string here, right?
I think we need to change ALGO_MAPPING to map binary strings to names of algorithms instead of mapping a byte (integer) to algorithms (i.e. we need to change . In nPA.py, for example, you can see a mapping from an OID (binary string) to some algorithm....
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Corrected as you suggested.