sysmo-usim-tool/card.py

187 lines
5.7 KiB
Python

#!/usr/bin/env python
# -*- coding: utf-8 -*-
"""
Smartcard Terminal IO Class
(C) 2017 by Sysmocom s.f.m.c. GmbH
All Rights Reserved
Author: Philipp Maier
This program 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, see <http://www.gnu.org/licenses/>.
"""
from smartcard.scard import *
import smartcard.util
from utils import *
# The following class abstract the terminal, however we reference it as "Card",
# because the terminal itsslef is not in the users interest, we are focussed
# on the card all the time. The abstraction done here is very simple and
# implements only the very basic functionality to communicate with a smartcard
# on APDU level.
#
# The classes Card_res_apdu and Card_apdu are helper classes in order to make
# passing the parameters/results simpler. They are not meant to be created
# anwhere else in the code. All handling is done through the Card class.
#
# The method apdu formats an APDU with its basic features (CLA, INS etc..)
# for the user. The user also may set an expected status word (Default is
# 0x9000). The status word is then checked when the transaction is performed
# using the transact method.
#
# The expected status word is a list of two bytes, each of the two bytes may be
# set to None. If this is the case the byte is not checked. If the whole list
# is set to none, the status word is not checked at all. If the status word
# check fails, the swok flag inside the repsonse apdu is set to false and an
# exception is thwron unless the strict parameter is not set to False.
#
# The user may set a dry-flag when calling the transact, then the transaction
# is not performed. Only the log line is printed. This is to verify if the
# transaction would be sent correctly, as some smartcad operations might
# be risky.
# Note: Programmed with the help of a blogpost from Ludovic Rousseau:
# https://ludovicrousseau.blogspot.de/2010/04/pcsc-sample-in-python.html
# A class to make handling of the responses a little simpler
# Note: Do not use directly, Card object will return responses.
class Card_res_apdu():
apdu = None
sw = None
swok = True
def __init__(self, apdu, sw):
self.apdu = apdu
self.sw = sw
def __str__(self):
dump = "APDU:" + hexdump(self.apdu)
dump += " SW:" + hexdump(self.sw)
return dump
# A class to make handling of the card input a little simpler
# Note: Do not use directly, use Card.apdu(...) instead
class Card_apdu():
apdu = None
sw = None
def __init__(self, cla, ins, p1, p2, p3, data, sw):
self.apdu = [cla, ins, p1, p2, p3]
if data:
self.apdu += data
self.sw = sw
def __str__(self):
dump = "APDU:" + hexdump(self.apdu)
return dump
# A class to abstract smartcard and terminal
class Card():
card = None
protocol = None
verbose = None
# Constructor: Set up connection to smartcard
def __init__(self, verbose = False):
self.verbose = verbose
# Eestablish a smartcard terminal context
hresult, hcontext = SCardEstablishContext(SCARD_SCOPE_USER)
if hresult != SCARD_S_SUCCESS:
print " * Error: Unable to establish smartcard terminal context -- exiting"
exit(1)
# Select the next available smartcard terminal
hresult, terminals = SCardListReaders(hcontext, [])
if hresult != SCARD_S_SUCCESS or len(terminals) < 1:
print " * Error: No smartcard terminal found -- exiting"
exit(1)
terminal = terminals[0]
print " * Terminal:", terminal
# Establish connection to smartcard
hresult, hcard, dwActiveProtocol = SCardConnect(hcontext,
terminal, SCARD_SHARE_SHARED,
SCARD_PROTOCOL_T0 | SCARD_PROTOCOL_T1)
if hresult != SCARD_S_SUCCESS:
print " * Error: No smartcard detected -- exiting"
exit(1)
print " * Protocol: " + str(dwActiveProtocol)
self.card = hcard
self.protocol = dwActiveProtocol
# Print debug info
def __debug_print(self, message):
if self.verbose:
print message
# Perform smartcard transaction
def transact(self, apdu, dry = False, strict = True):
# Dry run
if (dry):
self.__debug_print(" Card transaction: " + str(apdu)
+ " ==> Dry run: Transaction not executed")
return Card_res_apdu(None,None)
# Perform transaction
hresult, response = SCardTransmit(self.card,
self.protocol, apdu.apdu)
if hresult != SCARD_S_SUCCESS:
self.__debug_print(" * Card transaction: " + str(apdu)
+ " ==> Error: Smartcard transaction failed")
return Card_res_apdu(None,None)
res = Card_res_apdu(response[:-2],response[-2:])
self.__debug_print(" Card transaction: " + str(apdu)
+ " ==> " + str(res))
# Check status word
if apdu.sw:
if apdu.sw[0] and apdu.sw[0] == res.sw[0]:
res.swok = True
elif apdu.sw[0]:
res.swok = False
self.__debug_print(" * Warning: Unexpected status word (SW1)!")
if apdu.sw[1] and apdu.sw[1] == res.sw[1]:
res.swok = True
elif apdu.sw[1]:
res.swok = False
self.__debug_print(" * Warning: Unexpected status word (SW2)!")
# If strict mode is turned on, the status word must match,
# otherwise an exception is thrown
if strict and res.swok == False:
raise ValueError('Transaction failed!')
return res
# set up a new APDU
def apdu(self, cla, ins, p1 = 0x00, p2 = 0x00, p3 = 0x00,
data = None, sw = [0x90, 0x00]):
return Card_apdu(cla, ins, p1, p2, p3, data, sw)