APDU class, basically working

git-svn-id: svn+ssh://localhost/home/henryk/svn/cyberflex-shell/trunk@32 f711b948-2313-0410-aaa9-d29f33439f0b
This commit is contained in:
henryk 2005-10-06 23:35:46 +00:00
parent 9f8f494c36
commit 7802e9194f
2 changed files with 137 additions and 73 deletions

View File

@ -16,7 +16,7 @@ SECURE_CHANNEL_MACENC = 3
MAC_LENGTH = 8
class Cyberflex_Card(Java_Card):
APDU_INITIALIZE_UPDATE = APDU('\x80\x50\x00\x00\x08')
APDU_INITIALIZE_UPDATE = APDU('\x80\x50\x00\x00\x00')
APDU_EXTERNAL_AUTHENTICATE = APDU('\x84\x82\x00\x00')
APDU_GET_STATUS = APDU('\x84\xF2\x00\x00\x02\x4f\x00')
APDU_DELETE = APDU('\x84\xe4\x00\x00')
@ -112,7 +112,7 @@ class Cyberflex_Card(Java_Card):
host_challenge = crypto_utils.generate_host_challenge()
apdu = APDU(self.APDU_INITIALIZE_UPDATE,
p1 = keyset_version, p2 = key_index, lc = APDU.LC_AUTO,
p1 = keyset_version, p2 = key_index,
content = host_challenge)
self.secure_channel_state = SECURE_CHANNEL_NONE
@ -140,7 +140,7 @@ class Cyberflex_Card(Java_Card):
self.session_key_enc, card_challenge, host_challenge)
apdu = APDU(self.APDU_EXTERNAL_AUTHENTICATE,
p1 = security_level, p2 = 0, lc = APDU.LC_AUTO,
p1 = security_level, p2 = 0,
content = host_cryptogram)
self.secure_channel_state = SECURE_CHANNEL_MAC
@ -182,7 +182,7 @@ class Cyberflex_Card(Java_Card):
print "Cowardly refusing to delete the card manager."
raise ValueError, "Undeletable object"
tlvaid = chr(0x4f) + chr(len(aid)) + aid
apdu = APDU(self.APDU_DELETE, lc = APDU.LC_AUTO,
apdu = APDU(self.APDU_DELETE,
content = tlvaid)
result = self.send_apdu(apdu)

202
utils.py
View File

@ -86,117 +86,182 @@ def _unformat_hexdump(dump):
return binascii.a2b_hex("".join([e != " " and e or "" for e in hexdump]))
class APDU(list):
"""Class for an APDU that mostly behaves like a list."""
class APDU:
"""Class for an APDU.."""
OFFSET_CLA = 0
OFFSET_INS = 1
OFFSET_P1 = 2
OFFSET_P2 = 3
OFFSET_P3 = 4
OFFSET_LC = 4
OFFSET_LE = 4
OFFSET_CONTENT = 5
OFFSET_LC = 4
LC_AUTO = None
def __init__(self, *args, **kwargs):
"""Creates a new APDU instance. Can be given positional parameters which
must be sequences of either strings (or strings themselves) or integers
specifying byte values that will be concatenated in order. Alternatively
you may give exactly one positional argument that is an APDU instance.
The keyword arguments can then be used to override those values.
Keywords recognized are: cla, ins, p1, p2, p3, lc, le, content.
Note: only set the le parameter if you don't send data."""
After all the positional arguments have been concatenated they must
form a valid APDU!
if len(args) == 1 and type(args[0]) == APDU:
self.extend(args[0])
The keyword arguments can then be used to override those values.
Keywords recognized are: cla, ins, p1, p2, lc, le, content."""
initbuff = list()
if len(args) == 1 and isinstance(args[0], APDU):
initbuff.extend(args[0].get_string())
else:
for arg in args:
if type(arg) == str:
self.extend(arg)
initbuff.extend(arg)
elif hasattr(arg, "__iter__"):
for elem in arg:
if hasattr(elem, "__iter__"):
self.extend(elem)
initbuff.extend(elem)
else:
self.append(elem)
initbuff.append(elem)
else:
self.append(arg)
initbuff.append(arg)
if len(self) < 4:
self.extend([0] * (4-len(self)))
if len(self) < self.OFFSET_LC+1:
self[self.OFFSET_LC:self.OFFSET_LC+1] = [self.LC_AUTO]
for i in range(len(initbuff)):
t = type(initbuff[i])
if t == str:
initbuff[i] = ord(initbuff[i])
elif t != int:
raise TypeError, "APDU must consist of ints or one-byte strings, not %s (index %s)" % (t, i)
if len(initbuff) < 4:
initbuff.extend( [0] * (4-len(initbuff)) )
self.__dict__.update( {
"cla": initbuff[self.OFFSET_CLA],
"ins": initbuff[self.OFFSET_INS],
"p1": initbuff[self.OFFSET_P1],
"p2": initbuff[self.OFFSET_P2]
} )
lc_was_set = False
if len(initbuff) == 4: ## ISO case 1
self.le = 0
self.lc = 0
self.content = list()
elif len(initbuff) == 5: ## ISO case 2
self.le = initbuff[self.OFFSET_LE]
self.lc = 0
self.content = list()
elif len(initbuff) > 5:
self.lc = initbuff[self.OFFSET_LC]
lc_was_set = True
if len(initbuff) == 5 + self.lc: ## ISO case 3
self.le = 0
self.content = initbuff[5:5+self.lc]
elif len(initbuff) == 5 + self.lc + 1: ## ISO case 4
self.le = initbuff[-1]
self.content = initbuff[5:5+self.lc]
else:
raise ValueError, "Invalid APDU, length(%i) != 4 + 1 + lc(%i) + 1" % (len(initbuff), self.lc)
else:
raise ValueError, "Invalid APDU, impossible"
le = None
for (kw, arg) in kwargs.items():
if kw == "cla":
self[self.OFFSET_CLA] = arg
self.cla = arg
elif kw == "ins":
self[self.OFFSET_INS] = arg
self.ins = arg
elif kw == "p1":
self[self.OFFSET_P1] = arg
self.p1 = arg
elif kw == "p2":
self[self.OFFSET_P2] = arg
elif kw == "p3":
self[self.OFFSET_P3:self.OFFSET_P3+1] = (arg,)
self.p2 = arg
elif kw == "lc":
self[self.OFFSET_LC:self.OFFSET_LC+1] = (arg,)
self.lc = arg
lc_was_set = True
elif kw == "le":
le = arg
self.le = arg
elif kw == "content":
self[self.OFFSET_CONTENT:self.OFFSET_CONTENT+len(arg)] = arg
self.content = arg
else:
raise TypeError, "Got an unexpected keyword argument '%s'" % kw
if le is not None:
if len(self) > self.OFFSET_CONTENT:
raise TypeError, "le can't be set when there is data to send"
else:
self[self.OFFSET_LE:self.OFFSET_LE+1] = (le,)
if self[self.OFFSET_LC] == self.LC_AUTO:
if len(self) > self.OFFSET_CONTENT:
self[self.OFFSET_LC] = len(self)-self.OFFSET_CONTENT
else:
del self[self.OFFSET_LC]
for i in range(len(self)):
t = type(self[i])
if t == str:
self[i] = ord(self[i])
elif t != int:
raise TypeError, "APDU must consist of ints or one-byte strings, not %s (index %s)" % (t, i)
if not lc_was_set:
self.lc = len(self.content)
def __str__(self):
result = "APDU(CLA=0x%x, INS=0x%x, P1=0x%x, P2=0x%x" % (
self[self.OFFSET_CLA], self[self.OFFSET_INS],
self[self.OFFSET_P1], self[self.OFFSET_P2])
if len(self) == self.OFFSET_CONTENT:
result = result + ", LE=0x%x)" % self[self.OFFSET_LE]
elif len(self) > self.OFFSET_CONTENT:
result = result + ", LC=0x%x) with %i(0x%x) bytes of contents" % (
self[self.OFFSET_LC], len(self)-self.OFFSET_CONTENT, len(self)-self.OFFSET_CONTENT)
else:
result = len(self.content) != self.lc and "Invalid " or ""
result = result + "APDU(CLA=0x%x, INS=0x%x, P1=0x%x, P2=0x%x" % (
self.cla, self.ins, self.p1, self.p2)
if self.lc == 0 and self.le == 0: ## ISO case 1
result = result + ")"
elif self.lc == 0 and self.le > 0: ## ISO case 2:
result = result + ", LE=0x%x)" % self.le
elif self.lc > 0 and self.le == 0: ## ISO case 3
result = result + ", LC=0x%x)" % self.lc
elif self.lc > 0 and self.le > 0: ## ISO case 4:
result = result + ", LC=0x%x, LE=0x%x)" % (
self.lc, self.le
)
else:
raise ValueError, "Impossible error. Call the X files."
if len(self.content) > 0:
result = result + " with %i(0x%x) bytes of contents" % (
len(self.content), len(self.content)
)
return result + ":\n" + hexdump(self.get_string())
def __repr__(self):
result = "APDU(cla=0x%x, ins=0x%x, p1=0x%x, p2=0x%x" % (
self[self.OFFSET_CLA], self[self.OFFSET_INS],
self[self.OFFSET_P1], self[self.OFFSET_P2])
if len(self) == self.OFFSET_CONTENT:
result = result + ", le=0x%x)" % self[self.OFFSET_LE]
elif len(self) > self.OFFSET_CONTENT:
result = result + ", lc=0x%x, content=%s)" % (
self[self.OFFSET_LC], self[self.OFFSET_CONTENT:])
result = "APDU(CLA=0x%x, INS=0x%x, P1=0x%x, P2=0x%x" % (
self.cla, self.ins, self.p1, self.p2)
if self.lc == 0 and self.le == 0: ## ISO case 1
pass
elif self.lc == 0 and self.le > 0: ## ISO case 2:
result = result + ", LE=0x%x" % self.le
elif self.lc > 0 and self.le == 0: ## ISO case 3
result = result + ", LC=0x%x" % self.lc
elif self.lc > 0 and self.le > 0: ## ISO case 4:
result = result + ", LC=0x%x, LE=0x%x" % (
self.lc, self.le
)
else:
raise ValueError, "Impossible error. Call the X files."
if len(self.content) > 0:
result = result + ", content=%r)" % self.content
else:
result = result + ")"
return result
_bytevars = ("cla", "ins", "p1", "p2", "lc", "le")
_bytelistvars = ("content",)
def __setattr__(self, name, value):
namelower = name.lower()
if namelower in self._bytevars:
if isinstance(value, int):
self.__dict__[namelower] = value
elif isinstance(value, str):
self.__dict__[namelower] = ord(value)
else:
raise ValueError, "'%s' attribute can only be a byte, that is: int or str, not %s" % (namelower, type(value))
elif namelower in self._bytelistvars:
if isinstance(value, str):
self.__dict__[namelower] = [ord(e) for e in value]
elif isinstance(value, list):
self.__dict__[namelower] = [int(e) for e in value]
else:
raise ValueError, "'%s' attribute can only be a byte list, that is: list of int or str, not %s" (namelower, type(value))
else:
self.__dict__[name] = value
def get_string(self):
"""Return the contents of this APDU as a binary string."""
return "".join([i is not None and chr(i) or "?" for i in self])
contents = [self.cla, self.ins, self.p1, self.p2]
if self.lc > 0:
contents.extend( [self.lc] + self.content)
if self.le > 0:
contents.append( self.le )
return "".join([i is not None and chr(i) or "?" for i in contents])
if __name__ == "__main__":
response = """
@ -249,7 +314,6 @@ if __name__ == "__main__":
#response = sys.stdin.read()
#parse_status(_unformat_hexdump(response)[:-2])
print APDU((1,2,3), cla=0x23, content="hallo", lc=None)
print APDU(1,2,3,4,None,4,6)
print APDU((1,2,3), cla=0x23, content="hallo")
print APDU(1,2,3,4,2,4,6)