- Object orientated refactoring of CardGenerator
- PassportOS class in VirtualSmartcard.py is no longer needed, because all the functionallity is now in CardGenerator - Removed hard coded image - Added version number and type to file format git-svn-id: https://vsmartcard.svn.sourceforge.net/svnroot/vsmartcard@38 96b47cad-a561-4643-ad3b-153ac7d7599c
This commit is contained in:
@@ -17,11 +17,11 @@
|
|||||||
# virtualsmartcard. If not, see <http://www.gnu.org/licenses/>.
|
# virtualsmartcard. If not, see <http://www.gnu.org/licenses/>.
|
||||||
#
|
#
|
||||||
|
|
||||||
import sys, getpass, anydbm
|
import sys, getpass, anydbm, readline, os, dircache
|
||||||
from pickle import loads, dumps
|
from pickle import loads, dumps
|
||||||
from TLVutils import pack
|
from TLVutils import pack
|
||||||
from utils import inttostring
|
from utils import inttostring
|
||||||
from SmartcardFilesystem import MF, TransparentStructureEF
|
from SmartcardFilesystem import *
|
||||||
|
|
||||||
# pgp directory
|
# pgp directory
|
||||||
#self.mf.append(DF(parent=self.mf,
|
#self.mf.append(DF(parent=self.mf,
|
||||||
@@ -34,133 +34,164 @@ from SmartcardFilesystem import MF, TransparentStructureEF
|
|||||||
#self.mf.append(DF(parent=self.mf,
|
#self.mf.append(DF(parent=self.mf,
|
||||||
#fid=3, dfname='\xa0\x00\x00\x03\x08\x00\x00\x10\x00\x01\x00'))
|
#fid=3, dfname='\xa0\x00\x00\x03\x08\x00\x00\x10\x00\x01\x00'))
|
||||||
|
|
||||||
def generate_iso_card():
|
class CardGenerator(object):
|
||||||
from SmartcardSAM import SAM
|
|
||||||
|
|
||||||
print "Using default SAM. Insecure!!!"
|
def __init__(self, type='iso7816', sam=None, mf=None):
|
||||||
sam = SAM("1234", "1234567890") #FIXME: Use user provided data
|
types = ['iso7816', 'ePass', 'cryptoflex']
|
||||||
|
if not type in types:
|
||||||
|
raise ValueError, "Unsupported type " % type
|
||||||
|
|
||||||
mf = MF(filedescriptor=FDB["DF"], lifecycle=LCB["ACTIVATED"], dfname=None)
|
self.type = type
|
||||||
self.SAM.set_MF(self.mf)
|
self.mf = None
|
||||||
|
self.sam = None
|
||||||
|
|
||||||
return mf, sam
|
def __generate_iso_card(self):
|
||||||
|
from SmartcardSAM import SAM
|
||||||
|
|
||||||
def generate_ePass():
|
print "Using default SAM. Insecure!!!"
|
||||||
from PIL import Image
|
self.sam = SAM("1234", "1234567890") #FIXME: Use user provided data
|
||||||
from SmartcardFilesystem import DF
|
|
||||||
from SmartcardSAM import PassportSAM
|
|
||||||
|
|
||||||
MRZ1 = "P<UTOERIKSSON<<ANNA<MARIX<<<<<<<<<<<<<<<<<<<"
|
self.mf = MF(filedescriptor=FDB["DF"], lifecycle=LCB["ACTIVATED"], dfname=None)
|
||||||
MRZ2 = "L898902C<3UTO6908061F9406236ZE184226B<<<<<14"
|
self.sam.set_MF(self.mf)
|
||||||
MRZ = MRZ1 + MRZ2
|
|
||||||
|
|
||||||
picturepath = "jp2.jpg"
|
def __generate_ePass(self):
|
||||||
try:
|
from PIL import Image
|
||||||
im = Image.open(picturepath)
|
from SmartcardSAM import PassportSAM
|
||||||
pic_width, pic_height = im.size
|
|
||||||
fd = open(picturepath,"rb")
|
|
||||||
picture = fd.read()
|
|
||||||
fd.close()
|
|
||||||
except IOError:
|
|
||||||
print "Could not find picture %s" % picturepath
|
|
||||||
pic_width = 0
|
|
||||||
pic_height = 0
|
|
||||||
picture = None
|
|
||||||
|
|
||||||
mf = MF()
|
MRZ = raw_input("Please enter the MRZ as one string: ") #TODO: Sanity checks
|
||||||
|
|
||||||
#We need a MF with Application DF \xa0\x00\x00\x02G\x10\x01
|
readline.set_completer_delims("")
|
||||||
mf.append(DF(parent=mf, fid=4, dfname='\xa0\x00\x00\x02G\x10\x01', bertlv_data=[]))
|
readline.parse_and_bind("tab: complete")
|
||||||
df = mf.currentDF()
|
|
||||||
mf.append(TransparentStructureEF(parent=df, fid=0x011E, filedescriptor=0, data=""))#EF.COM
|
|
||||||
mf.append(TransparentStructureEF(parent=df, fid=0x0101, filedescriptor=0, data=""))#EF.DG1
|
|
||||||
mf.append(TransparentStructureEF(parent=df, fid=0x0102, filedescriptor=0, data=""))#EF.DG2
|
|
||||||
mf.append(TransparentStructureEF(parent=df, fid=0x010D, filedescriptor=0, data=""))#EF.SOD
|
|
||||||
#EF.COM
|
|
||||||
COM = pack([(0x5F01,4,"0107"),(0x5F36,6,"040000"),(0x5C,2,"6175")])
|
|
||||||
COM = pack(((0x60,len(COM),COM),))
|
|
||||||
fid = df.select("fid",0x011E)
|
|
||||||
fid.writebinary([0],[COM])
|
|
||||||
#EF.DG1
|
|
||||||
DG1 = pack([(0x5F1F,len(MRZ),MRZ)])
|
|
||||||
DG1 = pack([(0x61,len(DG1),DG1)])
|
|
||||||
fid = df.select("fid",0x0101)
|
|
||||||
fid.writebinary([0],[DG1])
|
|
||||||
#EF.DG2
|
|
||||||
if picture != None:
|
|
||||||
IIB = "\x00\x01" + inttostring(pic_width,2) + inttostring(pic_height,2) + 6 * "\x00"
|
|
||||||
length = 32 + len(picture) #32 is the length of IIB + FIB
|
|
||||||
FIB = inttostring(length,4) + 16 * "\x00"
|
|
||||||
FRH = "FAC" + "\x00" + "010" + "\x00" + inttostring(14+length,4) + inttostring(1,2)
|
|
||||||
picture = FRH + FIB + IIB + picture
|
|
||||||
DG2 = pack([(0xA1,8,"\x87\x02\x01\x01\x88\x02\x05\x01"),(0x5F2E,len(picture),picture)])
|
|
||||||
DG2 = pack([(0x02,1,"\x01"),(0x7F60,len(DG2),DG2)])
|
|
||||||
DG2 = pack([(0x7F61,len(DG2),DG2)])
|
|
||||||
fid = df.select("fid",0x0102)
|
|
||||||
fid.writebinary([0],[DG2])
|
|
||||||
|
|
||||||
sam = PassportSAM(mf)
|
picturepath = raw_input("Please enter the path to an image: ")
|
||||||
return mf, sam
|
picturepath = picturepath.rstrip() #FIXME
|
||||||
|
|
||||||
def generate_cryptoflex():
|
#MRZ1 = "P<UTOERIKSSON<<ANNA<MARIX<<<<<<<<<<<<<<<<<<<"
|
||||||
from SmartcardFilesystem import CryptoflexMF
|
#MRZ2 = "L898902C<3UTO6908061F9406236ZE184226B<<<<<14"
|
||||||
from SmartcardSAM import CryptoflexSAM
|
#MRZ = MRZ1 + MRZ2
|
||||||
|
|
||||||
mf = CryptoflexMF()
|
try:
|
||||||
mf.append(TransparentStructureEF(parent=mf, fid=0x0002, filedescriptor=0x01,
|
im = Image.open(picturepath)
|
||||||
data="\x00\x00\x00\x01\x00\x01\x00\x00")) #EF.ICCSN
|
pic_width, pic_height = im.size
|
||||||
sam = CryptoflexSAM(mf)
|
fd = open(picturepath,"rb")
|
||||||
|
picture = fd.read()
|
||||||
|
fd.close()
|
||||||
|
except IOError:
|
||||||
|
print "Failed to open file: " + picturepath
|
||||||
|
pic_width = 0
|
||||||
|
pic_height = 0
|
||||||
|
picture = None
|
||||||
|
|
||||||
return mf, sam
|
mf = MF()
|
||||||
|
|
||||||
def loadCard(filename, password=None):
|
#We need a MF with Application DF \xa0\x00\x00\x02G\x10\x01
|
||||||
from CryptoUtils import read_protected_string
|
mf.append(DF(parent=mf, fid=4, dfname='\xa0\x00\x00\x02G\x10\x01', bertlv_data=[]))
|
||||||
|
df = mf.currentDF()
|
||||||
|
mf.append(TransparentStructureEF(parent=df, fid=0x011E, filedescriptor=0, data=""))#EF.COM
|
||||||
|
mf.append(TransparentStructureEF(parent=df, fid=0x0101, filedescriptor=0, data=""))#EF.DG1
|
||||||
|
mf.append(TransparentStructureEF(parent=df, fid=0x0102, filedescriptor=0, data=""))#EF.DG2
|
||||||
|
mf.append(TransparentStructureEF(parent=df, fid=0x010D, filedescriptor=0, data=""))#EF.SOD
|
||||||
|
#EF.COM
|
||||||
|
COM = pack([(0x5F01,4,"0107"),(0x5F36,6,"040000"),(0x5C,2,"6175")])
|
||||||
|
COM = pack(((0x60,len(COM),COM),))
|
||||||
|
fid = df.select("fid",0x011E)
|
||||||
|
fid.writebinary([0],[COM])
|
||||||
|
#EF.DG1
|
||||||
|
DG1 = pack([(0x5F1F,len(MRZ),MRZ)])
|
||||||
|
DG1 = pack([(0x61,len(DG1),DG1)])
|
||||||
|
fid = df.select("fid",0x0101)
|
||||||
|
fid.writebinary([0],[DG1])
|
||||||
|
#EF.DG2
|
||||||
|
if picture != None:
|
||||||
|
IIB = "\x00\x01" + inttostring(pic_width,2) + inttostring(pic_height,2) + 6 * "\x00"
|
||||||
|
length = 32 + len(picture) #32 is the length of IIB + FIB
|
||||||
|
FIB = inttostring(length,4) + 16 * "\x00"
|
||||||
|
FRH = "FAC" + "\x00" + "010" + "\x00" + inttostring(14+length,4) + inttostring(1,2)
|
||||||
|
picture = FRH + FIB + IIB + picture
|
||||||
|
DG2 = pack([(0xA1,8,"\x87\x02\x01\x01\x88\x02\x05\x01"),(0x5F2E,len(picture),picture)])
|
||||||
|
DG2 = pack([(0x02,1,"\x01"),(0x7F60,len(DG2),DG2)])
|
||||||
|
DG2 = pack([(0x7F61,len(DG2),DG2)])
|
||||||
|
fid = df.select("fid",0x0102)
|
||||||
|
fid.writebinary([0],[DG2])
|
||||||
|
|
||||||
try:
|
self.mf = mf
|
||||||
db = anydbm.open(filename, 'r')
|
self.sam = PassportSAM(self.mf)
|
||||||
except anydbm.error:
|
|
||||||
print "Failed to open " + filename
|
|
||||||
|
|
||||||
if password is None:
|
def __generate_cryptoflex(self):
|
||||||
password = getpass.getpass("Please enter your password.")
|
from SmartcardSAM import CryptoflexSAM
|
||||||
|
|
||||||
serializedMF = read_protected_string(db["mf"], password)
|
self.mf = CryptoflexMF()
|
||||||
serializedSAM = read_protected_string(db["sam"], password)
|
self.mf.append(TransparentStructureEF(parent=self.mf, fid=0x0002, filedescriptor=0x01,
|
||||||
SAM = loads(serializedSAM)
|
data="\x00\x00\x00\x01\x00\x01\x00\x00")) #EF.ICCSN
|
||||||
MF = loads(serializedMF)
|
self.sam = CryptoflexSAM(self.mf)
|
||||||
#self.type = db["type"]
|
|
||||||
|
|
||||||
return SAM, MF
|
def generateCard(self):
|
||||||
|
if self.type == 'iso7816':
|
||||||
def saveCard(mf, sam, filename, password=None):
|
self.__generate_iso_card()
|
||||||
from CryptoUtils import protect_string
|
elif self.type == 'ePass':
|
||||||
|
self.__generate_ePass()
|
||||||
if filename != None:
|
elif self.type == 'cryptoflex':
|
||||||
print "Saving smartcard configuration to %s" % filename
|
self.__generate_cryptoflex()
|
||||||
else: #TODO: Ask user for filename
|
|
||||||
pass
|
|
||||||
|
|
||||||
if password is None:
|
|
||||||
passwd1 = getpass.getpass("Please enter a password.")
|
|
||||||
passwd2 = getpass.getpass("Please retype your password.")
|
|
||||||
if (passwd1 != passwd2):
|
|
||||||
raise ValueError, "Passwords did not match. Will now exit"
|
|
||||||
else:
|
else:
|
||||||
password = passwd1
|
raise ValueError, "Unsupported card type " % self.type
|
||||||
|
|
||||||
mf_string = dumps(mf)
|
def getCard(self):
|
||||||
sam_string = dumps(sam)
|
if self.sam is None or self.mf is None:
|
||||||
protectedMF = protect_string(mf_string, password)
|
self.generateCard()
|
||||||
protectedSAM = protect_string(sam_string, password)
|
return self.mf, self.sam
|
||||||
|
|
||||||
try:
|
def setCard(self, mf=None, sam=None):
|
||||||
db = anydbm.open(filename, 'c')
|
if mf != None:
|
||||||
db["mf"] = protectedMF
|
self.mf = mf
|
||||||
db["sam"] = protectedSAM
|
if sam != None:
|
||||||
#db["type"] = self.type
|
self.sam = sam
|
||||||
db.close()
|
|
||||||
except anydbm.error:
|
|
||||||
print "Failed to write data to disk"
|
def loadCard(self, filename, password=None):
|
||||||
|
from CryptoUtils import read_protected_string
|
||||||
|
|
||||||
|
try:
|
||||||
|
db = anydbm.open(filename, 'r')
|
||||||
|
except anydbm.error:
|
||||||
|
print "Failed to open " + filename
|
||||||
|
|
||||||
|
if password is None:
|
||||||
|
password = getpass.getpass("Please enter your password.")
|
||||||
|
|
||||||
|
serializedMF = read_protected_string(db["mf"], password)
|
||||||
|
serializedSAM = read_protected_string(db["sam"], password)
|
||||||
|
self.sam = loads(serializedSAM)
|
||||||
|
self.mf = loads(serializedMF)
|
||||||
|
self.type = db["type"]
|
||||||
|
|
||||||
|
def saveCard(self, filename, password=None):
|
||||||
|
from CryptoUtils import protect_string
|
||||||
|
|
||||||
|
if password is None:
|
||||||
|
passwd1 = getpass.getpass("Please enter a password.")
|
||||||
|
passwd2 = getpass.getpass("Please retype your password.")
|
||||||
|
if (passwd1 != passwd2):
|
||||||
|
raise ValueError, "Passwords did not match. Will now exit"
|
||||||
|
else:
|
||||||
|
password = passwd1
|
||||||
|
|
||||||
|
if self.mf == None or self.sam == None: #TODO: Sanity checks
|
||||||
|
raise ValueError, "Card Generator was not set up properly (missing mf or sam)"
|
||||||
|
|
||||||
|
mf_string = dumps(self.mf)
|
||||||
|
sam_string = dumps(self.sam)
|
||||||
|
protectedMF = protect_string(mf_string, password)
|
||||||
|
protectedSAM = protect_string(sam_string, password)
|
||||||
|
|
||||||
|
try:
|
||||||
|
db = anydbm.open(filename, 'c')
|
||||||
|
db["mf"] = protectedMF
|
||||||
|
db["sam"] = protectedSAM
|
||||||
|
db["type"] = self.type
|
||||||
|
db["version"] = "0.1"
|
||||||
|
db.close()
|
||||||
|
except anydbm.error:
|
||||||
|
print "Failed to write data to disk"
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
from optparse import OptionParser
|
from optparse import OptionParser
|
||||||
@@ -178,11 +209,7 @@ if __name__ == "__main__":
|
|||||||
print "You have to provide a filename using the -f option"
|
print "You have to provide a filename using the -f option"
|
||||||
sys.exit()
|
sys.exit()
|
||||||
|
|
||||||
if options.type == 'iso7816':
|
generator = CardGenerator(options.type)
|
||||||
mf, sam = generate_iso_card()
|
generator.generateCard()
|
||||||
elif options.type == 'ePass':
|
generator.saveCard(options.filename)
|
||||||
mf, sam = generate_ePass()
|
|
||||||
elif options.type == 'cryptoflex':
|
|
||||||
mf, sam = generate_cryptoflex()
|
|
||||||
|
|
||||||
saveCard(mf, sam, options.filename)
|
|
||||||
@@ -21,21 +21,21 @@ from TLVutils import *
|
|||||||
from SWutils import SwError, SW
|
from SWutils import SwError, SW
|
||||||
from SmartcardFilesystem import prettyprint_anything, MF, DF, CryptoflexMF, TransparentStructureEF
|
from SmartcardFilesystem import prettyprint_anything, MF, DF, CryptoflexMF, TransparentStructureEF
|
||||||
from utils import C_APDU, R_APDU, hexdump, inttostring
|
from utils import C_APDU, R_APDU, hexdump, inttostring
|
||||||
from SmartcardSAM import CardContainer, SAM, PassportSAM, CryptoflexSAM
|
from SmartcardSAM import SAM, PassportSAM, CryptoflexSAM
|
||||||
|
import CardGenerator
|
||||||
|
|
||||||
from pickle import dumps, loads
|
from pickle import dumps, loads
|
||||||
import socket, struct, sys, signal, atexit, traceback
|
import socket, struct, sys, signal, atexit, traceback
|
||||||
import struct
|
import struct, getpass, anydbm
|
||||||
import getpass
|
|
||||||
import shelve, anydbm
|
|
||||||
|
|
||||||
class SmartcardOS(object): # {{{
|
class SmartcardOS(object): # {{{
|
||||||
def __init__(self, mf=None, sam=None, ins2handler=None, maxle=MAX_SHORT_LE):
|
def __init__(self, mf, sam, ins2handler=None, maxle=MAX_SHORT_LE):
|
||||||
from CardGenerator import generate_iso_card
|
|
||||||
self.mf = mf
|
self.mf = mf
|
||||||
self.SAM = sam
|
self.SAM = sam
|
||||||
|
|
||||||
if self.mf == None and self.SAM == None:
|
#if self.mf == None and self.SAM == None:
|
||||||
self.mf, self.SAM = generate_iso_card()
|
# self.mf, self.SAM = generate_iso_card()
|
||||||
|
|
||||||
if not ins2handler:
|
if not ins2handler:
|
||||||
self.ins2handler = {
|
self.ins2handler = {
|
||||||
@@ -295,39 +295,8 @@ class SmartcardOS(object): # {{{
|
|||||||
return answer
|
return answer
|
||||||
# }}}
|
# }}}
|
||||||
|
|
||||||
class PassportOS(SmartcardOS):
|
|
||||||
"""
|
|
||||||
The PassportOS emulates a Passport Application according to ICAO MRTD standard.
|
|
||||||
It generates a data structure ...
|
|
||||||
It also integrates a SAM derived from the standard SmartcardSAM, providing the
|
|
||||||
Basic Access Control (BAC) mechanisms.
|
|
||||||
"""
|
|
||||||
|
|
||||||
def __init__(self, mf=None, sam=None, ins2handler=None, maxle=MAX_SHORT_LE):
|
|
||||||
from CardGenerator import generate_ePass
|
|
||||||
|
|
||||||
if mf == None and sam == None:
|
|
||||||
mf, sam = generate_ePass()
|
|
||||||
else: #TODO: Sanity check if mf or sam are provided
|
|
||||||
if mf == None:
|
|
||||||
mf, tmp = generate_ePass()
|
|
||||||
if sam == None:
|
|
||||||
sam = PassportSAM(mf)
|
|
||||||
SmartcardOS.__init__(self, mf=mf, sam=sam, ins2handler=ins2handler, maxle=maxle)
|
|
||||||
|
|
||||||
class CryptoflexOS(SmartcardOS): # {{{
|
class CryptoflexOS(SmartcardOS): # {{{
|
||||||
def __init__(self, mf=None, sam=None, ins2handler=None, maxle=MAX_SHORT_LE):
|
def __init__(self, mf, sam, ins2handler=None, maxle=MAX_SHORT_LE):
|
||||||
from GenerateCard import generate_cryptoflex
|
|
||||||
|
|
||||||
if mf == None and sam == None:
|
|
||||||
mf, sam = generate_cryptoflex()
|
|
||||||
else:
|
|
||||||
if mf == None:
|
|
||||||
mf, tmp = generate_cryptoflex()
|
|
||||||
del tmp
|
|
||||||
if sam == None:
|
|
||||||
sam = CryptoflexSAM(mf)
|
|
||||||
|
|
||||||
SmartcardOS.__init__(self, mf, sam, ins2handler, maxle)
|
SmartcardOS.__init__(self, mf, sam, ins2handler, maxle)
|
||||||
self.atr = '\x3B\xE2\x00\x00\x40\x20\x49\x06'
|
self.atr = '\x3B\xE2\x00\x00\x40\x20\x49\x06'
|
||||||
|
|
||||||
@@ -387,11 +356,9 @@ class VirtualICC(object): # {{{
|
|||||||
|
|
||||||
def __init__(self, filename, type, lenlen=3, host="localhost", port=35963):
|
def __init__(self, filename, type, lenlen=3, host="localhost", port=35963):
|
||||||
from os.path import exists
|
from os.path import exists
|
||||||
from CardGenerator import loadCard
|
|
||||||
|
|
||||||
self.filename = None
|
self.filename = None
|
||||||
SAM = None
|
self.cardGenerator = CardGenerator.CardGenerator(type)
|
||||||
MF = None
|
|
||||||
|
|
||||||
#If a filename is specified, try to load the card from disk
|
#If a filename is specified, try to load the card from disk
|
||||||
if filename == None:
|
if filename == None:
|
||||||
@@ -399,16 +366,15 @@ class VirtualICC(object): # {{{
|
|||||||
else:
|
else:
|
||||||
self.filename = filename
|
self.filename = filename
|
||||||
if exists(filename):
|
if exists(filename):
|
||||||
print "Found existing card " + filename + ". Will try to open it"
|
self.cardGenerator.loadCard(self.filename)
|
||||||
SAM, MF = loadCard(filename)
|
|
||||||
else:
|
else:
|
||||||
print "No file " + filename + " found. Will create new file at termination of program."
|
print "No file " + self.filename + " found. Will create new file at termination of program."
|
||||||
|
|
||||||
|
MF, SAM = self.cardGenerator.getCard()
|
||||||
|
|
||||||
#Generate an OS object of the correct type
|
#Generate an OS object of the correct type
|
||||||
if type == "iso7816":
|
if type == "iso7816" or type == "ePass":
|
||||||
self.os = SmartcardOS(MF, SAM)
|
self.os = SmartcardOS(MF, SAM)
|
||||||
elif type == "ePass":
|
|
||||||
self.os = PassportOS(MF, SAM)
|
|
||||||
elif type == "cryptoflex":
|
elif type == "cryptoflex":
|
||||||
self.os = CryptoflexOS(MF, SAM)
|
self.os = CryptoflexOS(MF, SAM)
|
||||||
else:
|
else:
|
||||||
@@ -431,9 +397,10 @@ class VirtualICC(object): # {{{
|
|||||||
#atexit.register(saveCard)
|
#atexit.register(saveCard)
|
||||||
|
|
||||||
def signalHandler(self, signal=None, frame=None):
|
def signalHandler(self, signal=None, frame=None):
|
||||||
from CardGenerator import saveCard
|
|
||||||
self.sock.close()
|
self.sock.close()
|
||||||
saveCard(self.os.mf, self.os.SAM, self.filename)
|
if self.filename != None:
|
||||||
|
self.cardGenerator.setCard(self.os.mf, self.os.SAM)
|
||||||
|
self.cardGenerator.saveCard(self.filename)
|
||||||
sys.exit()
|
sys.exit()
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
|
|||||||
Binary file not shown.
|
Before Width: | Height: | Size: 4.0 KiB |
Reference in New Issue
Block a user