forked from public/pysim
Compare commits
4 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 564f199c82 | |||
| 76fd765b73 | |||
| d51585863d | |||
| a8b16ffa7d |
@@ -1,2 +0,0 @@
|
||||
*.pyc
|
||||
.*.swp
|
||||
@@ -1,339 +0,0 @@
|
||||
GNU GENERAL PUBLIC LICENSE
|
||||
Version 2, June 1991
|
||||
|
||||
Copyright (C) 1989, 1991 Free Software Foundation, Inc.,
|
||||
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
Everyone is permitted to copy and distribute verbatim copies
|
||||
of this license document, but changing it is not allowed.
|
||||
|
||||
Preamble
|
||||
|
||||
The licenses for most software are designed to take away your
|
||||
freedom to share and change it. By contrast, the GNU General Public
|
||||
License is intended to guarantee your freedom to share and change free
|
||||
software--to make sure the software is free for all its users. This
|
||||
General Public License applies to most of the Free Software
|
||||
Foundation's software and to any other program whose authors commit to
|
||||
using it. (Some other Free Software Foundation software is covered by
|
||||
the GNU Lesser General Public License instead.) You can apply it to
|
||||
your programs, too.
|
||||
|
||||
When we speak of free software, we are referring to freedom, not
|
||||
price. Our General Public Licenses are designed to make sure that you
|
||||
have the freedom to distribute copies of free software (and charge for
|
||||
this service if you wish), that you receive source code or can get it
|
||||
if you want it, that you can change the software or use pieces of it
|
||||
in new free programs; and that you know you can do these things.
|
||||
|
||||
To protect your rights, we need to make restrictions that forbid
|
||||
anyone to deny you these rights or to ask you to surrender the rights.
|
||||
These restrictions translate to certain responsibilities for you if you
|
||||
distribute copies of the software, or if you modify it.
|
||||
|
||||
For example, if you distribute copies of such a program, whether
|
||||
gratis or for a fee, you must give the recipients all the rights that
|
||||
you have. You must make sure that they, too, receive or can get the
|
||||
source code. And you must show them these terms so they know their
|
||||
rights.
|
||||
|
||||
We protect your rights with two steps: (1) copyright the software, and
|
||||
(2) offer you this license which gives you legal permission to copy,
|
||||
distribute and/or modify the software.
|
||||
|
||||
Also, for each author's protection and ours, we want to make certain
|
||||
that everyone understands that there is no warranty for this free
|
||||
software. If the software is modified by someone else and passed on, we
|
||||
want its recipients to know that what they have is not the original, so
|
||||
that any problems introduced by others will not reflect on the original
|
||||
authors' reputations.
|
||||
|
||||
Finally, any free program is threatened constantly by software
|
||||
patents. We wish to avoid the danger that redistributors of a free
|
||||
program will individually obtain patent licenses, in effect making the
|
||||
program proprietary. To prevent this, we have made it clear that any
|
||||
patent must be licensed for everyone's free use or not licensed at all.
|
||||
|
||||
The precise terms and conditions for copying, distribution and
|
||||
modification follow.
|
||||
|
||||
GNU GENERAL PUBLIC LICENSE
|
||||
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
|
||||
|
||||
0. This License applies to any program or other work which contains
|
||||
a notice placed by the copyright holder saying it may be distributed
|
||||
under the terms of this General Public License. The "Program", below,
|
||||
refers to any such program or work, and a "work based on the Program"
|
||||
means either the Program or any derivative work under copyright law:
|
||||
that is to say, a work containing the Program or a portion of it,
|
||||
either verbatim or with modifications and/or translated into another
|
||||
language. (Hereinafter, translation is included without limitation in
|
||||
the term "modification".) Each licensee is addressed as "you".
|
||||
|
||||
Activities other than copying, distribution and modification are not
|
||||
covered by this License; they are outside its scope. The act of
|
||||
running the Program is not restricted, and the output from the Program
|
||||
is covered only if its contents constitute a work based on the
|
||||
Program (independent of having been made by running the Program).
|
||||
Whether that is true depends on what the Program does.
|
||||
|
||||
1. You may copy and distribute verbatim copies of the Program's
|
||||
source code as you receive it, in any medium, provided that you
|
||||
conspicuously and appropriately publish on each copy an appropriate
|
||||
copyright notice and disclaimer of warranty; keep intact all the
|
||||
notices that refer to this License and to the absence of any warranty;
|
||||
and give any other recipients of the Program a copy of this License
|
||||
along with the Program.
|
||||
|
||||
You may charge a fee for the physical act of transferring a copy, and
|
||||
you may at your option offer warranty protection in exchange for a fee.
|
||||
|
||||
2. You may modify your copy or copies of the Program or any portion
|
||||
of it, thus forming a work based on the Program, and copy and
|
||||
distribute such modifications or work under the terms of Section 1
|
||||
above, provided that you also meet all of these conditions:
|
||||
|
||||
a) You must cause the modified files to carry prominent notices
|
||||
stating that you changed the files and the date of any change.
|
||||
|
||||
b) You must cause any work that you distribute or publish, that in
|
||||
whole or in part contains or is derived from the Program or any
|
||||
part thereof, to be licensed as a whole at no charge to all third
|
||||
parties under the terms of this License.
|
||||
|
||||
c) If the modified program normally reads commands interactively
|
||||
when run, you must cause it, when started running for such
|
||||
interactive use in the most ordinary way, to print or display an
|
||||
announcement including an appropriate copyright notice and a
|
||||
notice that there is no warranty (or else, saying that you provide
|
||||
a warranty) and that users may redistribute the program under
|
||||
these conditions, and telling the user how to view a copy of this
|
||||
License. (Exception: if the Program itself is interactive but
|
||||
does not normally print such an announcement, your work based on
|
||||
the Program is not required to print an announcement.)
|
||||
|
||||
These requirements apply to the modified work as a whole. If
|
||||
identifiable sections of that work are not derived from the Program,
|
||||
and can be reasonably considered independent and separate works in
|
||||
themselves, then this License, and its terms, do not apply to those
|
||||
sections when you distribute them as separate works. But when you
|
||||
distribute the same sections as part of a whole which is a work based
|
||||
on the Program, the distribution of the whole must be on the terms of
|
||||
this License, whose permissions for other licensees extend to the
|
||||
entire whole, and thus to each and every part regardless of who wrote it.
|
||||
|
||||
Thus, it is not the intent of this section to claim rights or contest
|
||||
your rights to work written entirely by you; rather, the intent is to
|
||||
exercise the right to control the distribution of derivative or
|
||||
collective works based on the Program.
|
||||
|
||||
In addition, mere aggregation of another work not based on the Program
|
||||
with the Program (or with a work based on the Program) on a volume of
|
||||
a storage or distribution medium does not bring the other work under
|
||||
the scope of this License.
|
||||
|
||||
3. You may copy and distribute the Program (or a work based on it,
|
||||
under Section 2) in object code or executable form under the terms of
|
||||
Sections 1 and 2 above provided that you also do one of the following:
|
||||
|
||||
a) Accompany it with the complete corresponding machine-readable
|
||||
source code, which must be distributed under the terms of Sections
|
||||
1 and 2 above on a medium customarily used for software interchange; or,
|
||||
|
||||
b) Accompany it with a written offer, valid for at least three
|
||||
years, to give any third party, for a charge no more than your
|
||||
cost of physically performing source distribution, a complete
|
||||
machine-readable copy of the corresponding source code, to be
|
||||
distributed under the terms of Sections 1 and 2 above on a medium
|
||||
customarily used for software interchange; or,
|
||||
|
||||
c) Accompany it with the information you received as to the offer
|
||||
to distribute corresponding source code. (This alternative is
|
||||
allowed only for noncommercial distribution and only if you
|
||||
received the program in object code or executable form with such
|
||||
an offer, in accord with Subsection b above.)
|
||||
|
||||
The source code for a work means the preferred form of the work for
|
||||
making modifications to it. For an executable work, complete source
|
||||
code means all the source code for all modules it contains, plus any
|
||||
associated interface definition files, plus the scripts used to
|
||||
control compilation and installation of the executable. However, as a
|
||||
special exception, the source code distributed need not include
|
||||
anything that is normally distributed (in either source or binary
|
||||
form) with the major components (compiler, kernel, and so on) of the
|
||||
operating system on which the executable runs, unless that component
|
||||
itself accompanies the executable.
|
||||
|
||||
If distribution of executable or object code is made by offering
|
||||
access to copy from a designated place, then offering equivalent
|
||||
access to copy the source code from the same place counts as
|
||||
distribution of the source code, even though third parties are not
|
||||
compelled to copy the source along with the object code.
|
||||
|
||||
4. You may not copy, modify, sublicense, or distribute the Program
|
||||
except as expressly provided under this License. Any attempt
|
||||
otherwise to copy, modify, sublicense or distribute the Program is
|
||||
void, and will automatically terminate your rights under this License.
|
||||
However, parties who have received copies, or rights, from you under
|
||||
this License will not have their licenses terminated so long as such
|
||||
parties remain in full compliance.
|
||||
|
||||
5. You are not required to accept this License, since you have not
|
||||
signed it. However, nothing else grants you permission to modify or
|
||||
distribute the Program or its derivative works. These actions are
|
||||
prohibited by law if you do not accept this License. Therefore, by
|
||||
modifying or distributing the Program (or any work based on the
|
||||
Program), you indicate your acceptance of this License to do so, and
|
||||
all its terms and conditions for copying, distributing or modifying
|
||||
the Program or works based on it.
|
||||
|
||||
6. Each time you redistribute the Program (or any work based on the
|
||||
Program), the recipient automatically receives a license from the
|
||||
original licensor to copy, distribute or modify the Program subject to
|
||||
these terms and conditions. You may not impose any further
|
||||
restrictions on the recipients' exercise of the rights granted herein.
|
||||
You are not responsible for enforcing compliance by third parties to
|
||||
this License.
|
||||
|
||||
7. If, as a consequence of a court judgment or allegation of patent
|
||||
infringement or for any other reason (not limited to patent issues),
|
||||
conditions are imposed on you (whether by court order, agreement or
|
||||
otherwise) that contradict the conditions of this License, they do not
|
||||
excuse you from the conditions of this License. If you cannot
|
||||
distribute so as to satisfy simultaneously your obligations under this
|
||||
License and any other pertinent obligations, then as a consequence you
|
||||
may not distribute the Program at all. For example, if a patent
|
||||
license would not permit royalty-free redistribution of the Program by
|
||||
all those who receive copies directly or indirectly through you, then
|
||||
the only way you could satisfy both it and this License would be to
|
||||
refrain entirely from distribution of the Program.
|
||||
|
||||
If any portion of this section is held invalid or unenforceable under
|
||||
any particular circumstance, the balance of the section is intended to
|
||||
apply and the section as a whole is intended to apply in other
|
||||
circumstances.
|
||||
|
||||
It is not the purpose of this section to induce you to infringe any
|
||||
patents or other property right claims or to contest validity of any
|
||||
such claims; this section has the sole purpose of protecting the
|
||||
integrity of the free software distribution system, which is
|
||||
implemented by public license practices. Many people have made
|
||||
generous contributions to the wide range of software distributed
|
||||
through that system in reliance on consistent application of that
|
||||
system; it is up to the author/donor to decide if he or she is willing
|
||||
to distribute software through any other system and a licensee cannot
|
||||
impose that choice.
|
||||
|
||||
This section is intended to make thoroughly clear what is believed to
|
||||
be a consequence of the rest of this License.
|
||||
|
||||
8. If the distribution and/or use of the Program is restricted in
|
||||
certain countries either by patents or by copyrighted interfaces, the
|
||||
original copyright holder who places the Program under this License
|
||||
may add an explicit geographical distribution limitation excluding
|
||||
those countries, so that distribution is permitted only in or among
|
||||
countries not thus excluded. In such case, this License incorporates
|
||||
the limitation as if written in the body of this License.
|
||||
|
||||
9. The Free Software Foundation may publish revised and/or new versions
|
||||
of the General Public License from time to time. Such new versions will
|
||||
be similar in spirit to the present version, but may differ in detail to
|
||||
address new problems or concerns.
|
||||
|
||||
Each version is given a distinguishing version number. If the Program
|
||||
specifies a version number of this License which applies to it and "any
|
||||
later version", you have the option of following the terms and conditions
|
||||
either of that version or of any later version published by the Free
|
||||
Software Foundation. If the Program does not specify a version number of
|
||||
this License, you may choose any version ever published by the Free Software
|
||||
Foundation.
|
||||
|
||||
10. If you wish to incorporate parts of the Program into other free
|
||||
programs whose distribution conditions are different, write to the author
|
||||
to ask for permission. For software which is copyrighted by the Free
|
||||
Software Foundation, write to the Free Software Foundation; we sometimes
|
||||
make exceptions for this. Our decision will be guided by the two goals
|
||||
of preserving the free status of all derivatives of our free software and
|
||||
of promoting the sharing and reuse of software generally.
|
||||
|
||||
NO WARRANTY
|
||||
|
||||
11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
|
||||
FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
|
||||
OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
|
||||
PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
|
||||
OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
|
||||
MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
|
||||
TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
|
||||
PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
|
||||
REPAIR OR CORRECTION.
|
||||
|
||||
12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
|
||||
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
|
||||
REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
|
||||
INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
|
||||
OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
|
||||
TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
|
||||
YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
|
||||
PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
|
||||
POSSIBILITY OF SUCH DAMAGES.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
How to Apply These Terms to Your New Programs
|
||||
|
||||
If you develop a new program, and you want it to be of the greatest
|
||||
possible use to the public, the best way to achieve this is to make it
|
||||
free software which everyone can redistribute and change under these terms.
|
||||
|
||||
To do so, attach the following notices to the program. It is safest
|
||||
to attach them to the start of each source file to most effectively
|
||||
convey the exclusion of warranty; and each file should have at least
|
||||
the "copyright" line and a pointer to where the full notice is found.
|
||||
|
||||
<one line to give the program's name and a brief idea of what it does.>
|
||||
Copyright (C) <year> <name of author>
|
||||
|
||||
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, write to the Free Software Foundation, Inc.,
|
||||
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
|
||||
Also add information on how to contact you by electronic and paper mail.
|
||||
|
||||
If the program is interactive, make it output a short notice like this
|
||||
when it starts in an interactive mode:
|
||||
|
||||
Gnomovision version 69, Copyright (C) year name of author
|
||||
Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
|
||||
This is free software, and you are welcome to redistribute it
|
||||
under certain conditions; type `show c' for details.
|
||||
|
||||
The hypothetical commands `show w' and `show c' should show the appropriate
|
||||
parts of the General Public License. Of course, the commands you use may
|
||||
be called something other than `show w' and `show c'; they could even be
|
||||
mouse-clicks or menu items--whatever suits your program.
|
||||
|
||||
You should also get your employer (if you work as a programmer) or your
|
||||
school, if any, to sign a "copyright disclaimer" for the program, if
|
||||
necessary. Here is a sample; alter the names:
|
||||
|
||||
Yoyodyne, Inc., hereby disclaims all copyright interest in the program
|
||||
`Gnomovision' (which makes passes at compilers) written by James Hacker.
|
||||
|
||||
<signature of Ty Coon>, 1 April 1989
|
||||
Ty Coon, President of Vice
|
||||
|
||||
This General Public License does not permit incorporating your program into
|
||||
proprietary programs. If your program is a subroutine library, you may
|
||||
consider it more useful to permit linking proprietary applications with the
|
||||
library. If this is what you want to do, use the GNU Lesser General
|
||||
Public License instead of this License.
|
||||
@@ -1,38 +0,0 @@
|
||||
This utility allows to :
|
||||
|
||||
* Program customizable SIMs. Two modes are possible:
|
||||
|
||||
- one where you specify every parameter manually :
|
||||
|
||||
./pySim-prog.py -n 26C3 -c 49 -x 262 -y 42 -i <IMSI> -s <ICCID>
|
||||
|
||||
|
||||
- one where they are generated from some minimal set :
|
||||
|
||||
./pySim-prog.py -n 26C3 -c 49 -x 262 -y 42 -z <random_string_of_choice> -j <card_num>
|
||||
|
||||
With <random_string_of_choice> and <card_num>, the soft will generate
|
||||
'predictable' IMSI and ICCID, so make sure you choose them so as not to
|
||||
conflict with anyone. (for eg. your name as <random_string_of_choice> and
|
||||
0 1 2 ... for <card num>).
|
||||
|
||||
You also need to enter some parameters to select the device :
|
||||
-t TYPE : type of card (supersim, magicsim, fakemagicsim or try 'auto')
|
||||
-d DEV : Serial port device (default /dev/ttyUSB0)
|
||||
-b BAUD : Baudrate (default 9600)
|
||||
|
||||
* Interact with SIMs from a python interactive shell (ipython for eg :)
|
||||
|
||||
from pySim.transport.serial import SerialSimLink
|
||||
from pySim.commands import SimCardCommands
|
||||
|
||||
sl = SerialSimLink(device='/dev/ttyUSB0', baudrate=9600)
|
||||
sc = SimCardCommands(sl)
|
||||
|
||||
sl.wait_for_card()
|
||||
|
||||
# Print IMSI
|
||||
print sc.read_binary(['3f00', '7f20', '6f07'])
|
||||
|
||||
# Run A3/A8
|
||||
print sc.run_gsm('00112233445566778899aabbccddeeff')
|
||||
-627
@@ -1,627 +0,0 @@
|
||||
#!/usr/bin/env python
|
||||
|
||||
#
|
||||
# Utility to deal with sim cards and program the 'magic' ones easily
|
||||
#
|
||||
#
|
||||
# Part of the sim link code of inspired by pySimReader-Serial-src-v2
|
||||
#
|
||||
#
|
||||
# Copyright (C) 2009 Sylvain Munaut <tnt@246tNt.com>
|
||||
# Copyright (C) 2010 Harald Welte <laforge@gnumonks.org>
|
||||
#
|
||||
# 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/>.
|
||||
#
|
||||
|
||||
import hashlib
|
||||
from optparse import OptionParser
|
||||
import os
|
||||
import random
|
||||
import re
|
||||
import sys
|
||||
|
||||
try:
|
||||
import json
|
||||
except ImportError:
|
||||
# Python < 2.5
|
||||
import simplejson as json
|
||||
|
||||
from pySim.commands import SimCardCommands
|
||||
from pySim.cards import _cards_classes
|
||||
from pySim.utils import h2b, swap_nibbles, rpad
|
||||
|
||||
|
||||
def parse_options():
|
||||
|
||||
parser = OptionParser(usage="usage: %prog [options]")
|
||||
|
||||
parser.add_option("-d", "--device", dest="device", metavar="DEV",
|
||||
help="Serial Device for SIM access [default: %default]",
|
||||
default="/dev/ttyUSB0",
|
||||
)
|
||||
parser.add_option("-b", "--baud", dest="baudrate", type="int", metavar="BAUD",
|
||||
help="Baudrate used for SIM access [default: %default]",
|
||||
default=9600,
|
||||
)
|
||||
parser.add_option("-p", "--pcsc-device", dest="pcsc_dev", type='int', metavar="PCSC",
|
||||
help="Which PC/SC reader number for SIM access",
|
||||
default=None,
|
||||
)
|
||||
parser.add_option("-t", "--type", dest="type",
|
||||
help="Card type (user -t list to view) [default: %default]",
|
||||
default="auto",
|
||||
)
|
||||
parser.add_option("-e", "--erase", dest="erase", action='store_true',
|
||||
help="Erase beforehand [default: %default]",
|
||||
default=False,
|
||||
)
|
||||
|
||||
parser.add_option("-S", "--source", dest="source",
|
||||
help="Data Source[default: %default]",
|
||||
default="cmdline",
|
||||
)
|
||||
|
||||
# if mode is "cmdline"
|
||||
parser.add_option("-n", "--name", dest="name",
|
||||
help="Operator name [default: %default]",
|
||||
default="Magic",
|
||||
)
|
||||
parser.add_option("-c", "--country", dest="country", type="int", metavar="CC",
|
||||
help="Country code [default: %default]",
|
||||
default=1,
|
||||
)
|
||||
parser.add_option("-x", "--mcc", dest="mcc", type="int",
|
||||
help="Mobile Country Code [default: %default]",
|
||||
default=901,
|
||||
)
|
||||
parser.add_option("-y", "--mnc", dest="mnc", type="int",
|
||||
help="Mobile Network Code [default: %default]",
|
||||
default=55,
|
||||
)
|
||||
parser.add_option("-m", "--smsc", dest="smsc",
|
||||
help="SMSP [default: '00 + country code + 5555']",
|
||||
)
|
||||
parser.add_option("-M", "--smsp", dest="smsp",
|
||||
help="Raw SMSP content in hex [default: auto from SMSC]",
|
||||
)
|
||||
|
||||
parser.add_option("-s", "--iccid", dest="iccid", metavar="ID",
|
||||
help="Integrated Circuit Card ID",
|
||||
)
|
||||
parser.add_option("-i", "--imsi", dest="imsi",
|
||||
help="International Mobile Subscriber Identity",
|
||||
)
|
||||
parser.add_option("-k", "--ki", dest="ki",
|
||||
help="Ki (default is to randomize)",
|
||||
)
|
||||
parser.add_option("-o", "--opc", dest="opc",
|
||||
help="OPC (default is to randomize)",
|
||||
)
|
||||
parser.add_option("--op", dest="op",
|
||||
help="Set OP to derive OPC from OP and KI",
|
||||
)
|
||||
parser.add_option("--read-imsi", dest="read_imsi", action="store_true",
|
||||
help="Read the IMSI from the CARD", default=False
|
||||
)
|
||||
|
||||
|
||||
parser.add_option("-z", "--secret", dest="secret", metavar="STR",
|
||||
help="Secret used for ICCID/IMSI autogen",
|
||||
)
|
||||
parser.add_option("-j", "--num", dest="num", type=int,
|
||||
help="Card # used for ICCID/IMSI autogen",
|
||||
)
|
||||
parser.add_option("--batch", dest="batch_mode",
|
||||
help="Enable batch mode [default: %default]",
|
||||
default=False, action='store_true',
|
||||
)
|
||||
parser.add_option("--batch-state", dest="batch_state", metavar="FILE",
|
||||
help="Optional batch state file",
|
||||
)
|
||||
|
||||
# if mode is "csv"
|
||||
parser.add_option("--read-csv", dest="read_csv", metavar="FILE",
|
||||
help="Read parameters from CSV file rather than command line")
|
||||
|
||||
|
||||
parser.add_option("--write-csv", dest="write_csv", metavar="FILE",
|
||||
help="Append generated parameters in CSV file",
|
||||
)
|
||||
parser.add_option("--write-hlr", dest="write_hlr", metavar="FILE",
|
||||
help="Append generated parameters to OpenBSC HLR sqlite3",
|
||||
)
|
||||
parser.add_option("--dry-run", dest="dry_run",
|
||||
help="Perform a 'dry run', don't actually program the card",
|
||||
default=False, action="store_true")
|
||||
|
||||
(options, args) = parser.parse_args()
|
||||
|
||||
if options.type == 'list':
|
||||
for kls in _cards_classes:
|
||||
print kls.name
|
||||
sys.exit(0)
|
||||
|
||||
if options.source == 'csv':
|
||||
if (options.imsi is None) and (options.batch_mode is False) and (options.read_imsi is False):
|
||||
parser.error("CSV mode needs either an IMSI, --read-imsi or batch mode")
|
||||
if options.read_csv is None:
|
||||
parser.error("CSV mode requires a CSV input file")
|
||||
elif options.source == 'cmdline':
|
||||
if ((options.imsi is None) or (options.iccid is None)) and (options.num is None):
|
||||
parser.error("If either IMSI or ICCID isn't specified, num is required")
|
||||
else:
|
||||
parser.error("Only `cmdline' and `csv' sources supported")
|
||||
|
||||
if (options.read_csv is not None) and (options.source != 'csv'):
|
||||
parser.error("You cannot specify a CSV input file in source != csv")
|
||||
|
||||
if (options.batch_mode) and (options.num is None):
|
||||
options.num = 0
|
||||
|
||||
if (options.batch_mode):
|
||||
if (options.imsi is not None) or (options.iccid is not None):
|
||||
parser.error("Can't give ICCID/IMSI for batch mode, need to use automatic parameters ! see --num and --secret for more informations")
|
||||
|
||||
if args:
|
||||
parser.error("Extraneous arguments")
|
||||
|
||||
return options
|
||||
|
||||
|
||||
def _digits(secret, usage, len, num):
|
||||
s = hashlib.sha1(secret + usage + '%d' % num)
|
||||
d = ''.join(['%02d'%ord(x) for x in s.digest()])
|
||||
return d[0:len]
|
||||
|
||||
def _mcc_mnc_digits(mcc, mnc):
|
||||
return ('%03d%03d' if mnc > 100 else '%03d%02d') % (mcc, mnc)
|
||||
|
||||
def _cc_digits(cc):
|
||||
return ('%03d' if cc > 100 else '%02d') % cc
|
||||
|
||||
def _isnum(s, l=-1):
|
||||
return s.isdigit() and ((l== -1) or (len(s) == l))
|
||||
|
||||
def _ishex(s, l=-1):
|
||||
hc = '0123456789abcdef'
|
||||
return all([x in hc for x in s.lower()]) and ((l== -1) or (len(s) == l))
|
||||
|
||||
|
||||
def _dbi_binary_quote(s):
|
||||
# Count usage of each char
|
||||
cnt = {}
|
||||
for c in s:
|
||||
cnt[c] = cnt.get(c, 0) + 1
|
||||
|
||||
# Find best offset
|
||||
e = 0
|
||||
m = len(s)
|
||||
for i in range(1, 256):
|
||||
if i == 39:
|
||||
continue
|
||||
sum_ = cnt.get(i, 0) + cnt.get((i+1)&0xff, 0) + cnt.get((i+39)&0xff, 0)
|
||||
if sum_ < m:
|
||||
m = sum_
|
||||
e = i
|
||||
if m == 0: # No overhead ? use this !
|
||||
break;
|
||||
|
||||
# Generate output
|
||||
out = []
|
||||
out.append( chr(e) ) # Offset
|
||||
for c in s:
|
||||
x = (256 + ord(c) - e) % 256
|
||||
if x in (0, 1, 39):
|
||||
out.append('\x01')
|
||||
out.append(chr(x+1))
|
||||
else:
|
||||
out.append(chr(x))
|
||||
|
||||
return ''.join(out)
|
||||
|
||||
def calculate_luhn(cc):
|
||||
num = map(int, str(cc))
|
||||
check_digit = 10 - sum(num[-2::-2] + [sum(divmod(d * 2, 10)) for d in num[::-2]]) % 10
|
||||
return 0 if check_digit == 10 else check_digit
|
||||
|
||||
def derive_milenage_opc(ki_hex, op_hex):
|
||||
"""
|
||||
Run the milenage algorithm.
|
||||
"""
|
||||
from Crypto.Cipher import AES
|
||||
from Crypto.Util.strxor import strxor
|
||||
from pySim.utils import b2h
|
||||
|
||||
# We pass in hex string and now need to work on bytes
|
||||
aes = AES.new(h2b(ki_hex))
|
||||
opc_bytes = aes.encrypt(h2b(op_hex))
|
||||
return b2h(strxor(opc_bytes, h2b(op_hex)))
|
||||
|
||||
def gen_parameters(opts):
|
||||
"""Generates Name, ICCID, MCC, MNC, IMSI, SMSP, Ki from the
|
||||
options given by the user"""
|
||||
|
||||
# MCC/MNC
|
||||
mcc = opts.mcc
|
||||
mnc = opts.mnc
|
||||
|
||||
if not ((0 < mcc < 999) and (0 < mnc < 999)):
|
||||
raise ValueError('mcc & mnc must be between 0 and 999')
|
||||
|
||||
# Digitize country code (2 or 3 digits)
|
||||
cc_digits = _cc_digits(opts.country)
|
||||
|
||||
# Digitize MCC/MNC (5 or 6 digits)
|
||||
plmn_digits = _mcc_mnc_digits(mcc, mnc)
|
||||
|
||||
# ICCID (19 digits, E.118), though some phase1 vendors use 20 :(
|
||||
if opts.iccid is not None:
|
||||
iccid = opts.iccid
|
||||
if not _isnum(iccid, 19):
|
||||
raise ValueError('ICCID must be 19 digits !');
|
||||
|
||||
else:
|
||||
if opts.num is None:
|
||||
raise ValueError('Neither ICCID nor card number specified !')
|
||||
|
||||
iccid = (
|
||||
'89' + # Common prefix (telecom)
|
||||
cc_digits + # Country Code on 2/3 digits
|
||||
plmn_digits # MCC/MNC on 5/6 digits
|
||||
)
|
||||
|
||||
ml = 18 - len(iccid)
|
||||
|
||||
if opts.secret is None:
|
||||
# The raw number
|
||||
iccid += ('%%0%dd' % ml) % opts.num
|
||||
else:
|
||||
# Randomized digits
|
||||
iccid += _digits(opts.secret, 'ccid', ml, opts.num)
|
||||
|
||||
# Add checksum digit
|
||||
iccid += ('%1d' % calculate_luhn(iccid))
|
||||
|
||||
# IMSI (15 digits usually)
|
||||
if opts.imsi is not None:
|
||||
imsi = opts.imsi
|
||||
if not _isnum(imsi):
|
||||
raise ValueError('IMSI must be digits only !')
|
||||
|
||||
else:
|
||||
if opts.num is None:
|
||||
raise ValueError('Neither IMSI nor card number specified !')
|
||||
|
||||
ml = 15 - len(plmn_digits)
|
||||
|
||||
if opts.secret is None:
|
||||
# The raw number
|
||||
msin = ('%%0%dd' % ml) % opts.num
|
||||
else:
|
||||
# Randomized digits
|
||||
msin = _digits(opts.secret, 'imsi', ml, opts.num)
|
||||
|
||||
imsi = (
|
||||
plmn_digits + # MCC/MNC on 5/6 digits
|
||||
msin # MSIN
|
||||
)
|
||||
|
||||
# SMSP
|
||||
if opts.smsp is not None:
|
||||
smsp = opts.smsp
|
||||
if not _ishex(smsp):
|
||||
raise ValueError('SMSP must be hex digits only !')
|
||||
if len(smsp) < 28*2:
|
||||
raise ValueError('SMSP must be at least 28 bytes')
|
||||
|
||||
else:
|
||||
if opts.smsc is not None:
|
||||
smsc = opts.smsc
|
||||
if not _isnum(smsc):
|
||||
raise ValueError('SMSC must be digits only !')
|
||||
else:
|
||||
smsc = '00%d' % opts.country + '5555' # Hack ...
|
||||
|
||||
smsc = '%02d' % ((len(smsc) + 3)//2,) + "81" + swap_nibbles(rpad(smsc, 20))
|
||||
|
||||
smsp = (
|
||||
'e1' + # Parameters indicator
|
||||
'ff' * 12 + # TP-Destination address
|
||||
smsc + # TP-Service Centre Address
|
||||
'00' + # TP-Protocol identifier
|
||||
'00' + # TP-Data coding scheme
|
||||
'00' # TP-Validity period
|
||||
)
|
||||
|
||||
# Ki (random)
|
||||
if opts.ki is not None:
|
||||
ki = opts.ki
|
||||
if not re.match('^[0-9a-fA-F]{32}$', ki):
|
||||
raise ValueError('Ki needs to be 128 bits, in hex format')
|
||||
else:
|
||||
ki = ''.join(['%02x' % random.randrange(0,256) for i in range(16)])
|
||||
|
||||
# Ki (random)
|
||||
if opts.opc is not None:
|
||||
opc = opts.opc
|
||||
if not re.match('^[0-9a-fA-F]{32}$', opc):
|
||||
raise ValueError('OPC needs to be 128 bits, in hex format')
|
||||
|
||||
elif opts.op is not None:
|
||||
opc = derive_milenage_opc(ki, opts.op)
|
||||
else:
|
||||
opc = ''.join(['%02x' % random.randrange(0,256) for i in range(16)])
|
||||
|
||||
|
||||
# Return that
|
||||
return {
|
||||
'name' : opts.name,
|
||||
'iccid' : iccid,
|
||||
'mcc' : mcc,
|
||||
'mnc' : mnc,
|
||||
'imsi' : imsi,
|
||||
'smsp' : smsp,
|
||||
'ki' : ki,
|
||||
'opc' : opc,
|
||||
}
|
||||
|
||||
|
||||
def print_parameters(params):
|
||||
|
||||
print """Generated card parameters :
|
||||
> Name : %(name)s
|
||||
> SMSP : %(smsp)s
|
||||
> ICCID : %(iccid)s
|
||||
> MCC/MNC : %(mcc)d/%(mnc)d
|
||||
> IMSI : %(imsi)s
|
||||
> Ki : %(ki)s
|
||||
> OPC : %(opc)s
|
||||
""" % params
|
||||
|
||||
|
||||
def write_params_csv(opts, params):
|
||||
# csv
|
||||
if opts.write_csv:
|
||||
import csv
|
||||
row = ['name', 'iccid', 'mcc', 'mnc', 'imsi', 'smsp', 'ki', 'opc']
|
||||
f = open(opts.write_csv, 'a')
|
||||
cw = csv.writer(f)
|
||||
cw.writerow([params[x] for x in row])
|
||||
f.close()
|
||||
|
||||
def _read_params_csv(opts, imsi):
|
||||
import csv
|
||||
row = ['name', 'iccid', 'mcc', 'mnc', 'imsi', 'smsp', 'ki', 'opc']
|
||||
f = open(opts.read_csv, 'r')
|
||||
cr = csv.DictReader(f, row)
|
||||
i = 0
|
||||
for row in cr:
|
||||
if opts.num is not None and opts.read_imsi is False:
|
||||
if opts.num == i:
|
||||
f.close()
|
||||
return row;
|
||||
i += 1
|
||||
if row['imsi'] == imsi:
|
||||
f.close()
|
||||
return row;
|
||||
|
||||
f.close()
|
||||
return None
|
||||
|
||||
def read_params_csv(opts, imsi):
|
||||
row = _read_params_csv(opts, imsi)
|
||||
if row is not None:
|
||||
row['mcc'] = int(row['mcc'])
|
||||
row['mnc'] = int(row['mnc'])
|
||||
return row
|
||||
|
||||
|
||||
def write_params_hlr(opts, params):
|
||||
# SQLite3 OpenBSC HLR
|
||||
if opts.write_hlr:
|
||||
import sqlite3
|
||||
conn = sqlite3.connect(opts.write_hlr)
|
||||
|
||||
c = conn.execute(
|
||||
'INSERT INTO Subscriber ' +
|
||||
'(imsi, name, extension, authorized, created, updated) ' +
|
||||
'VALUES ' +
|
||||
'(?,?,?,1,datetime(\'now\'),datetime(\'now\'));',
|
||||
[
|
||||
params['imsi'],
|
||||
params['name'],
|
||||
'9' + params['iccid'][-5:-1]
|
||||
],
|
||||
)
|
||||
sub_id = c.lastrowid
|
||||
c.close()
|
||||
|
||||
c = conn.execute(
|
||||
'INSERT INTO AuthKeys ' +
|
||||
'(subscriber_id, algorithm_id, a3a8_ki)' +
|
||||
'VALUES ' +
|
||||
'(?,?,?)',
|
||||
[ sub_id, 2, sqlite3.Binary(_dbi_binary_quote(h2b(params['ki']))) ],
|
||||
)
|
||||
|
||||
conn.commit()
|
||||
conn.close()
|
||||
|
||||
def write_parameters(opts, params):
|
||||
write_params_csv(opts, params)
|
||||
write_params_hlr(opts, params)
|
||||
|
||||
|
||||
BATCH_STATE = [ 'name', 'country', 'mcc', 'mnc', 'smsp', 'secret', 'num' ]
|
||||
BATCH_INCOMPATIBLE = ['iccid', 'imsi', 'ki']
|
||||
|
||||
def init_batch(opts):
|
||||
# Need to do something ?
|
||||
if not opts.batch_mode:
|
||||
return
|
||||
|
||||
for k in BATCH_INCOMPATIBLE:
|
||||
if getattr(opts, k):
|
||||
print "Incompatible option with batch_state: %s" % (k,)
|
||||
sys.exit(-1)
|
||||
|
||||
# Don't load state if there is none ...
|
||||
if not opts.batch_state:
|
||||
return
|
||||
|
||||
if not os.path.isfile(opts.batch_state):
|
||||
print "No state file yet"
|
||||
return
|
||||
|
||||
# Get stored data
|
||||
fh = open(opts.batch_state)
|
||||
d = json.loads(fh.read())
|
||||
fh.close()
|
||||
|
||||
for k,v in d.iteritems():
|
||||
setattr(opts, k, v)
|
||||
|
||||
|
||||
def save_batch(opts):
|
||||
# Need to do something ?
|
||||
if not opts.batch_mode or not opts.batch_state:
|
||||
return
|
||||
|
||||
d = json.dumps(dict([(k,getattr(opts,k)) for k in BATCH_STATE]))
|
||||
fh = open(opts.batch_state, 'w')
|
||||
fh.write(d)
|
||||
fh.close()
|
||||
|
||||
|
||||
def card_detect(opts, scc):
|
||||
|
||||
# Detect type if needed
|
||||
card = None
|
||||
ctypes = dict([(kls.name, kls) for kls in _cards_classes])
|
||||
|
||||
if opts.type in ("auto", "auto_once"):
|
||||
for kls in _cards_classes:
|
||||
card = kls.autodetect(scc)
|
||||
if card:
|
||||
print "Autodetected card type %s" % card.name
|
||||
card.reset()
|
||||
break
|
||||
|
||||
if card is None:
|
||||
print "Autodetection failed"
|
||||
return
|
||||
|
||||
if opts.type == "auto_once":
|
||||
opts.type = card.name
|
||||
|
||||
elif opts.type in ctypes:
|
||||
card = ctypes[opts.type](scc)
|
||||
|
||||
else:
|
||||
raise ValueError("Unknown card type %s" % opts.type)
|
||||
|
||||
return card
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
|
||||
# Parse options
|
||||
opts = parse_options()
|
||||
|
||||
# Connect to the card
|
||||
if opts.pcsc_dev is None:
|
||||
from pySim.transport.serial import SerialSimLink
|
||||
sl = SerialSimLink(device=opts.device, baudrate=opts.baudrate)
|
||||
else:
|
||||
from pySim.transport.pcsc import PcscSimLink
|
||||
sl = PcscSimLink(opts.pcsc_dev)
|
||||
|
||||
# Create command layer
|
||||
scc = SimCardCommands(transport=sl)
|
||||
|
||||
# Batch mode init
|
||||
init_batch(opts)
|
||||
|
||||
# Iterate
|
||||
done = False
|
||||
first = True
|
||||
card = None
|
||||
|
||||
while not done:
|
||||
|
||||
if opts.dry_run is False:
|
||||
# Connect transport
|
||||
print "Insert card now (or CTRL-C to cancel)"
|
||||
sl.wait_for_card(newcardonly=not first)
|
||||
|
||||
# Not the first anymore !
|
||||
first = False
|
||||
|
||||
if opts.dry_run is False:
|
||||
# Get card
|
||||
card = card_detect(opts, scc)
|
||||
if card is None:
|
||||
if opts.batch_mode:
|
||||
first = False
|
||||
continue
|
||||
else:
|
||||
sys.exit(-1)
|
||||
|
||||
# Erase if requested
|
||||
if opts.erase:
|
||||
print "Formatting ..."
|
||||
card.erase()
|
||||
card.reset()
|
||||
|
||||
# Generate parameters
|
||||
if opts.source == 'cmdline':
|
||||
cp = gen_parameters(opts)
|
||||
elif opts.source == 'csv':
|
||||
if opts.read_imsi:
|
||||
if opts.dry_run:
|
||||
# Connect transport
|
||||
print "Insert card now (or CTRL-C to cancel)"
|
||||
sl.wait_for_card(newcardonly=not first)
|
||||
(res,_) = scc.read_binary(['3f00', '7f20', '6f07'])
|
||||
imsi = swap_nibbles(res)[3:]
|
||||
else:
|
||||
imsi = opts.imsi
|
||||
cp = read_params_csv(opts, imsi)
|
||||
if cp is None:
|
||||
print "Error reading parameters\n"
|
||||
sys.exit(2)
|
||||
print_parameters(cp)
|
||||
|
||||
if opts.dry_run is False:
|
||||
# Program the card
|
||||
print "Programming ..."
|
||||
if opts.dry_run is not True:
|
||||
card.program(cp)
|
||||
else:
|
||||
print "Dry Run: NOT PROGRAMMING!"
|
||||
|
||||
# Write parameters permanently
|
||||
write_parameters(opts, cp)
|
||||
|
||||
# Batch mode state update and save
|
||||
if opts.num is not None:
|
||||
opts.num += 1
|
||||
save_batch(opts)
|
||||
|
||||
# Done for this card and maybe for everything ?
|
||||
print "Done !\n"
|
||||
|
||||
if not opts.batch_mode:
|
||||
done = True
|
||||
|
||||
-351
@@ -1,351 +0,0 @@
|
||||
#!/usr/bin/env python
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
""" pySim: Card programmation logic
|
||||
"""
|
||||
|
||||
#
|
||||
# Copyright (C) 2009-2010 Sylvain Munaut <tnt@246tNt.com>
|
||||
# Copyright (C) 2011 Harald Welte <laforge@gnumonks.org>
|
||||
#
|
||||
# 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 pySim.utils import b2h, h2b, swap_nibbles, rpad, lpad
|
||||
|
||||
|
||||
class Card(object):
|
||||
|
||||
def __init__(self, scc):
|
||||
self._scc = scc
|
||||
|
||||
def _e_iccid(self, iccid):
|
||||
return swap_nibbles(rpad(iccid, 20))
|
||||
|
||||
def _e_imsi(self, imsi):
|
||||
"""Converts a string imsi into the value of the EF"""
|
||||
l = (len(imsi) + 1) // 2 # Required bytes
|
||||
oe = len(imsi) & 1 # Odd (1) / Even (0)
|
||||
ei = '%02x' % l + swap_nibbles(lpad('%01x%s' % ((oe<<3)|1, imsi), 16))
|
||||
return ei
|
||||
|
||||
def _e_plmn(self, mcc, mnc):
|
||||
"""Converts integer MCC/MNC into 6 bytes for EF"""
|
||||
return swap_nibbles(lpad('%d' % mcc, 3) + lpad('%d' % mnc, 3))
|
||||
|
||||
def reset(self):
|
||||
self._scc.reset_card()
|
||||
|
||||
|
||||
class _MagicSimBase(Card):
|
||||
"""
|
||||
Theses cards uses several record based EFs to store the provider infos,
|
||||
each possible provider uses a specific record number in each EF. The
|
||||
indexes used are ( where N is the number of providers supported ) :
|
||||
- [2 .. N+1] for the operator name
|
||||
- [1 .. N] for the programable EFs
|
||||
|
||||
* 3f00/7f4d/8f0c : Operator Name
|
||||
|
||||
bytes 0-15 : provider name, padded with 0xff
|
||||
byte 16 : length of the provider name
|
||||
byte 17 : 01 for valid records, 00 otherwise
|
||||
|
||||
* 3f00/7f4d/8f0d : Programmable Binary EFs
|
||||
|
||||
* 3f00/7f4d/8f0e : Programmable Record EFs
|
||||
|
||||
"""
|
||||
|
||||
@classmethod
|
||||
def autodetect(kls, scc):
|
||||
try:
|
||||
for p, l, t in kls._files.values():
|
||||
if not t:
|
||||
continue
|
||||
if scc.record_size(['3f00', '7f4d', p]) != l:
|
||||
return None
|
||||
except:
|
||||
return None
|
||||
|
||||
return kls(scc)
|
||||
|
||||
def _get_count(self):
|
||||
"""
|
||||
Selects the file and returns the total number of entries
|
||||
and entry size
|
||||
"""
|
||||
f = self._files['name']
|
||||
|
||||
r = self._scc.select_file(['3f00', '7f4d', f[0]])
|
||||
rec_len = int(r[-1][28:30], 16)
|
||||
tlen = int(r[-1][4:8],16)
|
||||
rec_cnt = (tlen / rec_len) - 1;
|
||||
|
||||
if (rec_cnt < 1) or (rec_len != f[1]):
|
||||
raise RuntimeError('Bad card type')
|
||||
|
||||
return rec_cnt
|
||||
|
||||
def program(self, p):
|
||||
# Go to dir
|
||||
self._scc.select_file(['3f00', '7f4d'])
|
||||
|
||||
# Home PLMN in PLMN_Sel format
|
||||
hplmn = self._e_plmn(p['mcc'], p['mnc'])
|
||||
|
||||
# Operator name ( 3f00/7f4d/8f0c )
|
||||
self._scc.update_record(self._files['name'][0], 2,
|
||||
rpad(b2h(p['name']), 32) + ('%02x' % len(p['name'])) + '01'
|
||||
)
|
||||
|
||||
# ICCID/IMSI/Ki/HPLMN ( 3f00/7f4d/8f0d )
|
||||
v = ''
|
||||
|
||||
# inline Ki
|
||||
if self._ki_file is None:
|
||||
v += p['ki']
|
||||
|
||||
# ICCID
|
||||
v += '3f00' + '2fe2' + '0a' + self._e_iccid(p['iccid'])
|
||||
|
||||
# IMSI
|
||||
v += '7f20' + '6f07' + '09' + self._e_imsi(p['imsi'])
|
||||
|
||||
# Ki
|
||||
if self._ki_file:
|
||||
v += self._ki_file + '10' + p['ki']
|
||||
|
||||
# PLMN_Sel
|
||||
v+= '6f30' + '18' + rpad(hplmn, 36)
|
||||
|
||||
self._scc.update_record(self._files['b_ef'][0], 1,
|
||||
rpad(v, self._files['b_ef'][1]*2)
|
||||
)
|
||||
|
||||
# SMSP ( 3f00/7f4d/8f0e )
|
||||
# FIXME
|
||||
|
||||
# Write PLMN_Sel forcefully as well
|
||||
r = self._scc.select_file(['3f00', '7f20', '6f30'])
|
||||
tl = int(r[-1][4:8], 16)
|
||||
|
||||
hplmn = self._e_plmn(p['mcc'], p['mnc'])
|
||||
self._scc.update_binary('6f30', hplmn + 'ff' * (tl-3))
|
||||
|
||||
def erase(self):
|
||||
# Dummy
|
||||
df = {}
|
||||
for k, v in self._files.iteritems():
|
||||
ofs = 1
|
||||
fv = v[1] * 'ff'
|
||||
if k == 'name':
|
||||
ofs = 2
|
||||
fv = fv[0:-4] + '0000'
|
||||
df[v[0]] = (fv, ofs)
|
||||
|
||||
# Write
|
||||
for n in range(0,self._get_count()):
|
||||
for k, (msg, ofs) in df.iteritems():
|
||||
self._scc.update_record(['3f00', '7f4d', k], n + ofs, msg)
|
||||
|
||||
|
||||
class SuperSim(_MagicSimBase):
|
||||
|
||||
name = 'supersim'
|
||||
|
||||
_files = {
|
||||
'name' : ('8f0c', 18, True),
|
||||
'b_ef' : ('8f0d', 74, True),
|
||||
'r_ef' : ('8f0e', 50, True),
|
||||
}
|
||||
|
||||
_ki_file = None
|
||||
|
||||
|
||||
class MagicSim(_MagicSimBase):
|
||||
|
||||
name = 'magicsim'
|
||||
|
||||
_files = {
|
||||
'name' : ('8f0c', 18, True),
|
||||
'b_ef' : ('8f0d', 130, True),
|
||||
'r_ef' : ('8f0e', 102, False),
|
||||
}
|
||||
|
||||
_ki_file = '6f1b'
|
||||
|
||||
|
||||
class FakeMagicSim(Card):
|
||||
"""
|
||||
Theses cards have a record based EF 3f00/000c that contains the provider
|
||||
informations. See the program method for its format. The records go from
|
||||
1 to N.
|
||||
"""
|
||||
|
||||
name = 'fakemagicsim'
|
||||
|
||||
@classmethod
|
||||
def autodetect(kls, scc):
|
||||
try:
|
||||
if scc.record_size(['3f00', '000c']) != 0x5a:
|
||||
return None
|
||||
except:
|
||||
return None
|
||||
|
||||
return kls(scc)
|
||||
|
||||
def _get_infos(self):
|
||||
"""
|
||||
Selects the file and returns the total number of entries
|
||||
and entry size
|
||||
"""
|
||||
|
||||
r = self._scc.select_file(['3f00', '000c'])
|
||||
rec_len = int(r[-1][28:30], 16)
|
||||
tlen = int(r[-1][4:8],16)
|
||||
rec_cnt = (tlen / rec_len) - 1;
|
||||
|
||||
if (rec_cnt < 1) or (rec_len != 0x5a):
|
||||
raise RuntimeError('Bad card type')
|
||||
|
||||
return rec_cnt, rec_len
|
||||
|
||||
def program(self, p):
|
||||
# Home PLMN
|
||||
r = self._scc.select_file(['3f00', '7f20', '6f30'])
|
||||
tl = int(r[-1][4:8], 16)
|
||||
|
||||
hplmn = self._e_plmn(p['mcc'], p['mnc'])
|
||||
self._scc.update_binary('6f30', hplmn + 'ff' * (tl-3))
|
||||
|
||||
# Get total number of entries and entry size
|
||||
rec_cnt, rec_len = self._get_infos()
|
||||
|
||||
# Set first entry
|
||||
entry = (
|
||||
'81' + # 1b Status: Valid & Active
|
||||
rpad(b2h(p['name'][0:14]), 28) + # 14b Entry Name
|
||||
self._e_iccid(p['iccid']) + # 10b ICCID
|
||||
self._e_imsi(p['imsi']) + # 9b IMSI_len + id_type(9) + IMSI
|
||||
p['ki'] + # 16b Ki
|
||||
lpad(p['smsp'], 80) # 40b SMSP (padded with ff if needed)
|
||||
)
|
||||
self._scc.update_record('000c', 1, entry)
|
||||
|
||||
def erase(self):
|
||||
# Get total number of entries and entry size
|
||||
rec_cnt, rec_len = self._get_infos()
|
||||
|
||||
# Erase all entries
|
||||
entry = 'ff' * rec_len
|
||||
for i in range(0, rec_cnt):
|
||||
self._scc.update_record('000c', 1+i, entry)
|
||||
|
||||
class GrcardSim(Card):
|
||||
"""
|
||||
Greencard (grcard.cn) HZCOS GSM SIM
|
||||
These cards have a much more regular ISO 7816-4 / TS 11.11 structure,
|
||||
and use standard UPDATE RECORD / UPDATE BINARY commands except for Ki.
|
||||
"""
|
||||
|
||||
name = 'grcardsim'
|
||||
|
||||
@classmethod
|
||||
def autodetect(kls, scc):
|
||||
return None
|
||||
|
||||
def program(self, p):
|
||||
# We don't really know yet what ADM PIN 4 is about
|
||||
#self._scc.verify_chv(4, h2b("4444444444444444"))
|
||||
|
||||
# Authenticate using ADM PIN 5
|
||||
self._scc.verify_chv(5, h2b("4444444444444444"))
|
||||
|
||||
# EF.ICCID
|
||||
r = self._scc.select_file(['3f00', '2fe2'])
|
||||
data, sw = self._scc.update_binary('2fe2', self._e_iccid(p['iccid']))
|
||||
|
||||
# EF.IMSI
|
||||
r = self._scc.select_file(['3f00', '7f20', '6f07'])
|
||||
data, sw = self._scc.update_binary('6f07', self._e_imsi(p['imsi']))
|
||||
|
||||
# EF.ACC
|
||||
#r = self._scc.select_file(['3f00', '7f20', '6f78'])
|
||||
#self._scc.update_binary('6f78', self._e_imsi(p['imsi'])
|
||||
|
||||
# EF.SMSP
|
||||
r = self._scc.select_file(['3f00', '7f10', '6f42'])
|
||||
data, sw = self._scc.update_record('6f42', 1, lpad(p['smsp'], 80))
|
||||
|
||||
# Set the Ki using proprietary command
|
||||
pdu = '80d4020010' + p['ki']
|
||||
data, sw = self._scc._tp.send_apdu(pdu)
|
||||
|
||||
# EF.HPLMN
|
||||
r = self._scc.select_file(['3f00', '7f20', '6f30'])
|
||||
size = int(r[-1][4:8], 16)
|
||||
hplmn = self._e_plmn(p['mcc'], p['mnc'])
|
||||
self._scc.update_binary('6f30', hplmn + 'ff' * (size-3))
|
||||
|
||||
# EF.SPN (Service Provider Name)
|
||||
r = self._scc.select_file(['3f00', '7f20', '6f30'])
|
||||
size = int(r[-1][4:8], 16)
|
||||
# FIXME
|
||||
|
||||
# FIXME: EF.MSISDN
|
||||
|
||||
def erase(self):
|
||||
return
|
||||
|
||||
class SysmoSIMgr1(GrcardSim):
|
||||
"""
|
||||
sysmocom sysmoSIM-GR1
|
||||
These cards have a much more regular ISO 7816-4 / TS 11.11 structure,
|
||||
and use standard UPDATE RECORD / UPDATE BINARY commands except for Ki.
|
||||
"""
|
||||
name = 'sysmosim-gr1'
|
||||
|
||||
# In order for autodetection ...
|
||||
|
||||
class SysmoUSIMgr1(Card):
|
||||
"""
|
||||
sysmocom sysmoUSIM-GR1
|
||||
"""
|
||||
name = 'sysmoUSIM-GR1'
|
||||
|
||||
@classmethod
|
||||
def autodetect(kls, scc):
|
||||
# TODO: Access the ATR
|
||||
return None
|
||||
|
||||
def program(self, p):
|
||||
# TODO: check if verify_chv could be used or what it needs
|
||||
# self._scc.verify_chv(0x0A, [0x33,0x32,0x32,0x31,0x33,0x32,0x33,0x32])
|
||||
# Unlock the card..
|
||||
data, sw = self._scc._tp.send_apdu_checksw("0020000A083332323133323332")
|
||||
|
||||
# TODO: move into SimCardCommands
|
||||
par = ( p['ki'] + # 16b K
|
||||
p['opc'] + # 32b OPC
|
||||
self._e_iccid(p['iccid']) + # 10b ICCID
|
||||
self._e_imsi(p['imsi']) # 9b IMSI_len + id_type(9) + IMSI
|
||||
)
|
||||
data, sw = self._scc._tp.send_apdu_checksw("0099000033" + par)
|
||||
|
||||
def erase(self):
|
||||
return
|
||||
|
||||
_cards_classes = [ FakeMagicSim, SuperSim, MagicSim, GrcardSim,
|
||||
SysmoSIMgr1, SysmoUSIMgr1 ]
|
||||
@@ -1,95 +0,0 @@
|
||||
#!/usr/bin/env python
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
""" pySim: SIM Card commands according to ISO 7816-4 and TS 11.11
|
||||
"""
|
||||
|
||||
#
|
||||
# Copyright (C) 2009-2010 Sylvain Munaut <tnt@246tNt.com>
|
||||
# Copyright (C) 2010 Harald Welte <laforge@gnumonks.org>
|
||||
#
|
||||
# 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 pySim.utils import rpad, b2h
|
||||
|
||||
|
||||
class SimCardCommands(object):
|
||||
def __init__(self, transport):
|
||||
self._tp = transport;
|
||||
|
||||
def select_file(self, dir_list):
|
||||
rv = []
|
||||
for i in dir_list:
|
||||
data, sw = self._tp.send_apdu_checksw("a0a4000002" + i)
|
||||
rv.append(data)
|
||||
return rv
|
||||
|
||||
def read_binary(self, ef, length=None, offset=0):
|
||||
if not hasattr(type(ef), '__iter__'):
|
||||
ef = [ef]
|
||||
r = self.select_file(ef)
|
||||
if length is None:
|
||||
length = int(r[-1][4:8], 16) - offset
|
||||
pdu = 'a0b0%04x%02x' % (offset, (min(256, length) & 0xff))
|
||||
return self._tp.send_apdu(pdu)
|
||||
|
||||
def update_binary(self, ef, data, offset=0):
|
||||
if not hasattr(type(ef), '__iter__'):
|
||||
ef = [ef]
|
||||
self.select_file(ef)
|
||||
pdu = 'a0d6%04x%02x' % (offset, len(data)/2) + data
|
||||
return self._tp.send_apdu_checksw(pdu)
|
||||
|
||||
def read_record(self, ef, rec_no):
|
||||
if not hasattr(type(ef), '__iter__'):
|
||||
ef = [ef]
|
||||
r = self.select_file(ef)
|
||||
rec_length = int(r[-1][28:30], 16)
|
||||
pdu = 'a0b2%02x04%02x' % (rec_no, rec_length)
|
||||
return self._tp.send_apdu(pdu)
|
||||
|
||||
def update_record(self, ef, rec_no, data, force_len=False):
|
||||
if not hasattr(type(ef), '__iter__'):
|
||||
ef = [ef]
|
||||
r = self.select_file(ef)
|
||||
if not force_len:
|
||||
rec_length = int(r[-1][28:30], 16)
|
||||
if (len(data)/2 != rec_length):
|
||||
raise ValueError('Invalid data length (expected %d, got %d)' % (rec_length, len(data)/2))
|
||||
else:
|
||||
rec_length = len(data)/2
|
||||
pdu = ('a0dc%02x04%02x' % (rec_no, rec_length)) + data
|
||||
return self._tp.send_apdu_checksw(pdu)
|
||||
|
||||
def record_size(self, ef):
|
||||
r = self.select_file(ef)
|
||||
return int(r[-1][28:30], 16)
|
||||
|
||||
def record_count(self, ef):
|
||||
r = self.select_file(ef)
|
||||
return int(r[-1][4:8], 16) // int(r[-1][28:30], 16)
|
||||
|
||||
def run_gsm(self, rand):
|
||||
if len(rand) != 32:
|
||||
raise ValueError('Invalid rand')
|
||||
self.select_file(['3f00', '7f20'])
|
||||
return self._tp.send_apdu('a088000010' + rand)
|
||||
|
||||
def reset_card(self):
|
||||
return self._tp.reset_card()
|
||||
|
||||
def verify_chv(self, chv_no, code):
|
||||
fc = rpad(b2h(code), 16)
|
||||
return self._tp.send_apdu_checksw('a02000' + ('%02x' % chv_no) + '08' + fc)
|
||||
@@ -1,33 +0,0 @@
|
||||
#!/usr/bin/env python
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
""" pySim: Exceptions
|
||||
"""
|
||||
|
||||
#
|
||||
# Copyright (C) 2009-2010 Sylvain Munaut <tnt@246tNt.com>
|
||||
#
|
||||
# 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 __future__ import absolute_import
|
||||
|
||||
import exceptions
|
||||
|
||||
|
||||
class NoCardError(exceptions.Exception):
|
||||
pass
|
||||
|
||||
class ProtocolError(exceptions.Exception):
|
||||
pass
|
||||
@@ -1,88 +0,0 @@
|
||||
#!/usr/bin/env python
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
""" pySim: PCSC reader transport link base
|
||||
"""
|
||||
|
||||
#
|
||||
# Copyright (C) 2009-2010 Sylvain Munaut <tnt@246tNt.com>
|
||||
#
|
||||
# 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/>.
|
||||
#
|
||||
|
||||
class LinkBase(object):
|
||||
|
||||
def wait_for_card(self, timeout=None, newcardonly=False):
|
||||
"""wait_for_card(): Wait for a card and connect to it
|
||||
|
||||
timeout : Maximum wait time (None=no timeout)
|
||||
newcardonly : Should we wait for a new card, or an already
|
||||
inserted one ?
|
||||
"""
|
||||
pass
|
||||
|
||||
def connect(self):
|
||||
"""connect(): Connect to a card immediately
|
||||
"""
|
||||
pass
|
||||
|
||||
def disconnect(self):
|
||||
"""disconnect(): Disconnect from card
|
||||
"""
|
||||
pass
|
||||
|
||||
def reset_card(self):
|
||||
"""reset_card(): Resets the card (power down/up)
|
||||
"""
|
||||
pass
|
||||
|
||||
def send_apdu_raw(self, pdu):
|
||||
"""send_apdu_raw(pdu): Sends an APDU with minimal processing
|
||||
|
||||
pdu : string of hexadecimal characters (ex. "A0A40000023F00")
|
||||
return : tuple(data, sw), where
|
||||
data : string (in hex) of returned data (ex. "074F4EFFFF")
|
||||
sw : string (in hex) of status word (ex. "9000")
|
||||
"""
|
||||
pass
|
||||
|
||||
def send_apdu(self, pdu):
|
||||
"""send_apdu(pdu): Sends an APDU and auto fetch response data
|
||||
|
||||
pdu : string of hexadecimal characters (ex. "A0A40000023F00")
|
||||
return : tuple(data, sw), where
|
||||
data : string (in hex) of returned data (ex. "074F4EFFFF")
|
||||
sw : string (in hex) of status word (ex. "9000")
|
||||
"""
|
||||
data, sw = self.send_apdu_raw(pdu)
|
||||
|
||||
if (sw is not None) and (sw[0:2] == '9f'):
|
||||
pdu_gr = pdu[0:2] + 'c00000' + sw[2:4]
|
||||
data, sw = self.send_apdu_raw(pdu_gr)
|
||||
|
||||
return data, sw
|
||||
|
||||
def send_apdu_checksw(self, pdu, sw="9000"):
|
||||
"""send_apdu_checksw(pdu,sw): Sends an APDU and check returned SW
|
||||
|
||||
pdu : string of hexadecimal characters (ex. "A0A40000023F00")
|
||||
sw : string of 4 hexadecimal characters (ex. "9000")
|
||||
return : tuple(data, sw), where
|
||||
data : string (in hex) of returned data (ex. "074F4EFFFF")
|
||||
sw : string (in hex) of status word (ex. "9000")
|
||||
"""
|
||||
rv = self.send_apdu(pdu)
|
||||
if sw.lower() != rv[1]:
|
||||
raise RuntimeError("SW match failed ! Expected %s and got %s." % (sw.lower(), rv[1]))
|
||||
return rv
|
||||
@@ -1,80 +0,0 @@
|
||||
#!/usr/bin/env python
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
""" pySim: PCSC reader transport link
|
||||
"""
|
||||
|
||||
#
|
||||
# Copyright (C) 2009-2010 Sylvain Munaut <tnt@246tNt.com>
|
||||
# Copyright (C) 2010 Harald Welte <laforge@gnumonks.org>
|
||||
#
|
||||
# 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.CardRequest import CardRequest
|
||||
from smartcard.Exceptions import NoCardException, CardRequestTimeoutException
|
||||
from smartcard.System import readers
|
||||
|
||||
from pySim.exceptions import NoCardError
|
||||
from pySim.transport import LinkBase
|
||||
from pySim.utils import h2i, i2h
|
||||
|
||||
|
||||
class PcscSimLink(LinkBase):
|
||||
|
||||
def __init__(self, reader_number=0):
|
||||
r = readers();
|
||||
self._reader = r[reader_number]
|
||||
self._con = self._reader.createConnection()
|
||||
|
||||
def __del__(self):
|
||||
self._con.disconnect()
|
||||
return
|
||||
|
||||
def wait_for_card(self, timeout=None, newcardonly=False):
|
||||
cr = CardRequest(readers=[self._reader], timeout=timeout, newcardonly=newcardonly)
|
||||
try:
|
||||
cr.waitforcard()
|
||||
except CardRequestTimeoutException:
|
||||
raise NoCardError()
|
||||
self.connect()
|
||||
|
||||
def connect(self):
|
||||
try:
|
||||
self._con.connect()
|
||||
except NoCardException:
|
||||
raise NoCardError()
|
||||
|
||||
def disconnect(self):
|
||||
self._con.disconnect()
|
||||
|
||||
def reset_card(self):
|
||||
self._con.disconnect()
|
||||
try:
|
||||
self._con.connect()
|
||||
except NoCardException:
|
||||
raise NoCardError()
|
||||
return 1
|
||||
|
||||
def send_apdu_raw(self, pdu):
|
||||
"""see LinkBase.send_apdu_raw"""
|
||||
|
||||
apdu = h2i(pdu)
|
||||
|
||||
data, sw1, sw2 = self._con.transmit(apdu)
|
||||
|
||||
sw = [sw1, sw2]
|
||||
|
||||
# Return value
|
||||
return i2h(data), i2h(sw)
|
||||
@@ -1,224 +0,0 @@
|
||||
#!/usr/bin/env python
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
""" pySim: Transport Link for serial (RS232) based readers included with simcard
|
||||
"""
|
||||
|
||||
#
|
||||
# Copyright (C) 2009-2010 Sylvain Munaut <tnt@246tNt.com>
|
||||
#
|
||||
# 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 __future__ import absolute_import
|
||||
|
||||
import serial
|
||||
import time
|
||||
|
||||
from pySim.exceptions import NoCardError, ProtocolError
|
||||
from pySim.transport import LinkBase
|
||||
from pySim.utils import h2b, b2h
|
||||
|
||||
|
||||
class SerialSimLink(LinkBase):
|
||||
|
||||
def __init__(self, device='/dev/ttyUSB0', baudrate=9600, rst='-rts', debug=False):
|
||||
self._sl = serial.Serial(
|
||||
port = device,
|
||||
parity = serial.PARITY_EVEN,
|
||||
bytesize = serial.EIGHTBITS,
|
||||
stopbits = serial.STOPBITS_TWO,
|
||||
timeout = 1,
|
||||
xonxoff = 0,
|
||||
rtscts = 0,
|
||||
baudrate = baudrate,
|
||||
)
|
||||
self._rst_pin = rst
|
||||
self._debug = debug
|
||||
|
||||
def __del__(self):
|
||||
self._sl.close()
|
||||
|
||||
def wait_for_card(self, timeout=None, newcardonly=False):
|
||||
# Direct try
|
||||
existing = False
|
||||
|
||||
try:
|
||||
self.reset_card()
|
||||
if not newcardonly:
|
||||
return
|
||||
else:
|
||||
existing = True
|
||||
except NoCardError:
|
||||
pass
|
||||
|
||||
# Poll ...
|
||||
mt = time.time() + timeout if timeout is not None else None
|
||||
pe = 0
|
||||
|
||||
while (mt is None) or (time.time() < mt):
|
||||
try:
|
||||
time.sleep(0.5)
|
||||
self.reset_card()
|
||||
if not existing:
|
||||
return
|
||||
except NoCardError:
|
||||
existing = False
|
||||
except ProtocolError:
|
||||
if existing:
|
||||
existing = False
|
||||
else:
|
||||
# Tolerate a couple of protocol error ... can happen if
|
||||
# we try when the card is 'half' inserted
|
||||
pe += 1
|
||||
if (pe > 2):
|
||||
raise
|
||||
|
||||
# Timed out ...
|
||||
raise NoCardError()
|
||||
|
||||
def connect(self):
|
||||
self.reset_card()
|
||||
|
||||
def disconnect(self):
|
||||
pass # Nothing to do really ...
|
||||
|
||||
def reset_card(self):
|
||||
rv = self._reset_card()
|
||||
if rv == 0:
|
||||
raise NoCardError()
|
||||
elif rv < 0:
|
||||
raise ProtocolError()
|
||||
|
||||
def _reset_card(self):
|
||||
rst_meth_map = {
|
||||
'rts': self._sl.setRTS,
|
||||
'dtr': self._sl.setDTR,
|
||||
}
|
||||
rst_val_map = { '+':0, '-':1 }
|
||||
|
||||
try:
|
||||
rst_meth = rst_meth_map[self._rst_pin[1:]]
|
||||
rst_val = rst_val_map[self._rst_pin[0]]
|
||||
except:
|
||||
raise ValueError('Invalid reset pin %s' % self._rst_pin);
|
||||
|
||||
rst_meth(rst_val)
|
||||
time.sleep(0.1) # 100 ms
|
||||
self._sl.flushInput()
|
||||
rst_meth(rst_val ^ 1)
|
||||
|
||||
b = self._rx_byte()
|
||||
if not b:
|
||||
return 0
|
||||
if ord(b) != 0x3b:
|
||||
return -1;
|
||||
self._dbg_print("TS: 0x%x Direct convention" % ord(b))
|
||||
|
||||
while ord(b) == 0x3b:
|
||||
b = self._rx_byte()
|
||||
|
||||
if not b:
|
||||
return -1
|
||||
t0 = ord(b)
|
||||
self._dbg_print("T0: 0x%x" % t0)
|
||||
|
||||
for i in range(4):
|
||||
if t0 & (0x10 << i):
|
||||
self._dbg_print("T%si = %x" % (chr(ord('A')+i), ord(self._rx_byte())))
|
||||
|
||||
for i in range(0, t0 & 0xf):
|
||||
self._dbg_print("Historical = %x" % ord(self._rx_byte()))
|
||||
|
||||
while True:
|
||||
x = self._rx_byte()
|
||||
if not x:
|
||||
break
|
||||
self._dbg_print("Extra: %x" % ord(x))
|
||||
|
||||
return 1
|
||||
|
||||
def _dbg_print(self, s):
|
||||
if self._debug:
|
||||
print s
|
||||
|
||||
def _tx_byte(self, b):
|
||||
self._sl.write(b)
|
||||
r = self._sl.read()
|
||||
if r != b: # TX and RX are tied, so we must clear the echo
|
||||
raise ProtocolError("Bad echo value. Expected %02x, got %s)" % (ord(b), '%02x'%ord(r) if r else '(nil)'))
|
||||
|
||||
def _tx_string(self, s):
|
||||
"""This is only safe if it's guaranteed the card won't send any data
|
||||
during the time of tx of the string !!!"""
|
||||
self._sl.write(s)
|
||||
r = self._sl.read(len(s))
|
||||
if r != s: # TX and RX are tied, so we must clear the echo
|
||||
raise ProtocolError("Bad echo value (Expected: %s, got %s)" % (b2h(s), b2h(r)))
|
||||
|
||||
def _rx_byte(self):
|
||||
return self._sl.read()
|
||||
|
||||
def send_apdu_raw(self, pdu):
|
||||
"""see LinkBase.send_apdu_raw"""
|
||||
|
||||
pdu = h2b(pdu)
|
||||
data_len = ord(pdu[4]) # P3
|
||||
|
||||
# Send first CLASS,INS,P1,P2,P3
|
||||
self._tx_string(pdu[0:5])
|
||||
|
||||
# Wait ack which can be
|
||||
# - INS: Command acked -> go ahead
|
||||
# - 0x60: NULL, just wait some more
|
||||
# - SW1: The card can apparently proceed ...
|
||||
while True:
|
||||
b = self._rx_byte()
|
||||
if b == pdu[1]:
|
||||
break
|
||||
elif b != '\x60':
|
||||
# Ok, it 'could' be SW1
|
||||
sw1 = b
|
||||
sw2 = self._rx_byte()
|
||||
nil = self._rx_byte()
|
||||
if (sw2 and not nil):
|
||||
return '', b2h(sw1+sw2)
|
||||
|
||||
raise ProtocolError()
|
||||
|
||||
# Send data (if any)
|
||||
if len(pdu) > 5:
|
||||
self._tx_string(pdu[5:])
|
||||
|
||||
# Receive data (including SW !)
|
||||
# length = [P3 - tx_data (=len(pdu)-len(hdr)) + 2 (SW1/2) ]
|
||||
to_recv = data_len - len(pdu) + 5 + 2
|
||||
|
||||
data = ''
|
||||
while (len(data) < to_recv):
|
||||
b = self._rx_byte()
|
||||
if (to_recv == 2) and (b == '\x60'): # Ignore NIL if we have no RX data (hack ?)
|
||||
continue
|
||||
if not b:
|
||||
break;
|
||||
data += b
|
||||
|
||||
# Split datafield from SW
|
||||
if len(data) < 2:
|
||||
return None, None
|
||||
sw = data[-2:]
|
||||
data = data[0:-2]
|
||||
|
||||
# Return value
|
||||
return b2h(data), b2h(sw)
|
||||
@@ -1,44 +0,0 @@
|
||||
#!/usr/bin/env python
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
""" pySim: various utilities
|
||||
"""
|
||||
|
||||
#
|
||||
# Copyright (C) 2009-2010 Sylvain Munaut <tnt@246tNt.com>
|
||||
#
|
||||
# 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/>.
|
||||
#
|
||||
|
||||
|
||||
def h2b(s):
|
||||
return ''.join([chr((int(x,16)<<4)+int(y,16)) for x,y in zip(s[0::2], s[1::2])])
|
||||
|
||||
def b2h(s):
|
||||
return ''.join(['%02x'%ord(x) for x in s])
|
||||
|
||||
def h2i(s):
|
||||
return [(int(x,16)<<4)+int(y,16) for x,y in zip(s[0::2], s[1::2])]
|
||||
|
||||
def i2h(s):
|
||||
return ''.join(['%02x'%(x) for x in s])
|
||||
|
||||
def swap_nibbles(s):
|
||||
return ''.join([x+y for x,y in zip(s[1::2], s[0::2])])
|
||||
|
||||
def rpad(s, l, c='f'):
|
||||
return s + c * (l - len(s))
|
||||
|
||||
def lpad(s, l, c='f'):
|
||||
return c * (l - len(s)) + s
|
||||
Reference in New Issue
Block a user