forked from public/pysim
Compare commits
23 Commits
c1a55a1d8b
..
27c3
| Author | SHA1 | Date | |
|---|---|---|---|
| 454bd52572 | |||
| be6f44dc14 | |||
| 4876712f90 | |||
| 530076ebba | |||
| 9f120e0169 | |||
| 8d243e8847 | |||
| 98d2b85a97 | |||
| 8f7d3ba8ba | |||
| bdca252fb0 | |||
| 9c8729a2d1 | |||
| 245d534962 | |||
| 143e99d1c6 | |||
| 1771603806 | |||
| e9fdecb38b | |||
| e7c15cd088 | |||
| 76504e0a6e | |||
| be17997e55 | |||
| 6576ce1886 | |||
| 6dc5145663 | |||
| 63722fd99b | |||
| 36f7a2d60b | |||
| 995c398c13 | |||
| 0d1e63a978 |
@@ -0,0 +1,2 @@
|
||||
*.pyc
|
||||
.*.swp
|
||||
@@ -0,0 +1,339 @@
|
||||
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.
|
||||
@@ -0,0 +1,36 @@
|
||||
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)
|
||||
|
||||
# Print IMSI
|
||||
print sc.read_binary(['3f00', '7f20', '6f07'])
|
||||
|
||||
# Run A3/A8
|
||||
print sc.run_gsm('00112233445566778899aabbccddeeff')
|
||||
@@ -1,21 +0,0 @@
|
||||
{
|
||||
"comments": [
|
||||
{
|
||||
"unresolved": false,
|
||||
"key": {
|
||||
"uuid": "00440fee_5c4ac0f8",
|
||||
"filename": "/PATCHSET_LEVEL",
|
||||
"patchSetId": 1
|
||||
},
|
||||
"lineNbr": 0,
|
||||
"author": {
|
||||
"id": 1000028
|
||||
},
|
||||
"writtenOn": "2026-03-06T10:17:02Z",
|
||||
"side": 1,
|
||||
"message": "At the moment I am re-programming the keys manually to make the testcases in the follow-up patch pass. The problem is that the card sanitizer resets the keys to the original ones every 24h. So we need to be a bit careful when merging the follow up patches. I think the safest way would be to wait 24h before we continue with merging the follow up patches.",
|
||||
"revId": "b59e47c7be33fc8f273b75f9671cb0debf972359",
|
||||
"serverId": "035e6965-6537-41bd-912c-053f3cf69326"
|
||||
}
|
||||
]
|
||||
}
|
||||
Executable
+153
@@ -0,0 +1,153 @@
|
||||
#!/usr/bin/env python
|
||||
|
||||
#
|
||||
# Utility to write the cards
|
||||
#
|
||||
#
|
||||
# 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/>.
|
||||
#
|
||||
|
||||
from optparse import OptionParser
|
||||
|
||||
from pySim.commands import SimCardCommands
|
||||
from pySim.cards import _cards_classes
|
||||
|
||||
|
||||
|
||||
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
|
||||
|
||||
|
||||
#
|
||||
# Main
|
||||
#
|
||||
|
||||
def parse_options():
|
||||
|
||||
parser = OptionParser(usage="usage: %prog [options]")
|
||||
|
||||
# Card interface
|
||||
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",
|
||||
)
|
||||
|
||||
(options, args) = parser.parse_args()
|
||||
|
||||
if options.type == 'list':
|
||||
for kls in _cards_classes:
|
||||
print kls.name
|
||||
sys.exit(0)
|
||||
|
||||
if args:
|
||||
parser.error("Extraneous arguments")
|
||||
|
||||
return options
|
||||
|
||||
|
||||
def 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)
|
||||
|
||||
# Iterate
|
||||
done = False
|
||||
first = True
|
||||
card = None
|
||||
|
||||
while not done:
|
||||
# Connect transport
|
||||
print "Insert card now (or CTRL-C to cancel)"
|
||||
sl.wait_for_card(newcardonly=not first)
|
||||
|
||||
# Not the first anymore !
|
||||
first = False
|
||||
|
||||
# Get card
|
||||
card = card_detect(opts, scc)
|
||||
if card is None:
|
||||
if opts.batch_mode:
|
||||
first = False
|
||||
continue
|
||||
else:
|
||||
sys.exit(-1)
|
||||
|
||||
# Check type
|
||||
if card.name != 'fakemagicsim':
|
||||
print "Can't fix this type of card ..."
|
||||
continue
|
||||
|
||||
# Fix record
|
||||
data, sw = scc.read_record(['000c'], 1)
|
||||
data_new = data[0:100] + 'fffffffffffffffffffffffffdffffffffffffffffffffffff0791947106004034ffffffffffffff'
|
||||
scc.update_record(['000c'], 1, data_new)
|
||||
|
||||
# Done for this card and maybe for everything ?
|
||||
print "Card should be fixed now !\n"
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
Executable
+230
@@ -0,0 +1,230 @@
|
||||
#!/usr/bin/env python
|
||||
|
||||
#
|
||||
# Utility to generate the HLR
|
||||
#
|
||||
#
|
||||
# Copyright (C) 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 optparse import OptionParser
|
||||
|
||||
from ccc import StateManager, CardParametersGenerator, isnum
|
||||
from pySim.utils import h2b
|
||||
|
||||
|
||||
#
|
||||
# OpenBSC HLR Writing
|
||||
#
|
||||
|
||||
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 hlr_write_cards(filename, network, cards):
|
||||
|
||||
import sqlite3
|
||||
|
||||
conn = sqlite3.connect(filename)
|
||||
|
||||
for card in cards:
|
||||
c = conn.execute(
|
||||
'INSERT INTO Subscriber ' +
|
||||
'(imsi, name, extension, authorized, created, updated) ' +
|
||||
'VALUES ' +
|
||||
'(?,?,?,1,datetime(\'now\'),datetime(\'now\'));',
|
||||
[
|
||||
card.imsi,
|
||||
'%s #%d' % (network.name, card.num),
|
||||
'9%05d' % card.num,
|
||||
],
|
||||
)
|
||||
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(card.ki))) ],
|
||||
)
|
||||
c.close()
|
||||
|
||||
conn.commit()
|
||||
conn.close()
|
||||
|
||||
|
||||
#
|
||||
# CSV Writing
|
||||
#
|
||||
|
||||
def csv_write_cards(filename, network, cards):
|
||||
import csv
|
||||
fh = open(filename, 'a')
|
||||
cw = csv.writer(fh)
|
||||
cw.writerows(cards)
|
||||
fh.close()
|
||||
|
||||
|
||||
#
|
||||
# Main stuff
|
||||
#
|
||||
|
||||
def parse_options():
|
||||
|
||||
parser = OptionParser(usage="usage: %prog [options]")
|
||||
|
||||
# Network parameters
|
||||
parser.add_option("-n", "--name", dest="name",
|
||||
help="Operator name [default: %default]",
|
||||
default="CCC Event",
|
||||
)
|
||||
parser.add_option("-c", "--country", dest="country", type="int", metavar="CC",
|
||||
help="Country code [default: %default]",
|
||||
default=49,
|
||||
)
|
||||
parser.add_option("-x", "--mcc", dest="mcc", type="int",
|
||||
help="Mobile Country Code [default: %default]",
|
||||
default=262,
|
||||
)
|
||||
parser.add_option("-y", "--mnc", dest="mnc", type="int",
|
||||
help="Mobile Network Code [default: %default]",
|
||||
default=42,
|
||||
)
|
||||
parser.add_option("-m", "--smsp", dest="smsp",
|
||||
help="SMSP [default: '00 + country code + 5555']",
|
||||
)
|
||||
|
||||
# Autogen
|
||||
parser.add_option("-z", "--secret", dest="secret", metavar="STR",
|
||||
help="Secret used for ICCID/IMSI autogen",
|
||||
)
|
||||
parser.add_option("-k", "--count", dest="count", type="int", metavar="CNT",
|
||||
help="Number of entried to generate [default: %default]",
|
||||
default=1000,
|
||||
)
|
||||
|
||||
# Output
|
||||
parser.add_option("--state", dest="state_file", metavar="FILE",
|
||||
help="Use this state file",
|
||||
)
|
||||
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",
|
||||
)
|
||||
|
||||
(options, args) = parser.parse_args()
|
||||
|
||||
if args:
|
||||
parser.error("Extraneous arguments")
|
||||
|
||||
# Check everything
|
||||
if 1 < len(options.name) > 16:
|
||||
parser.error("Name must be between 1 and 16 characters")
|
||||
|
||||
if 0 < options.country > 999:
|
||||
parser.error("Invalid country code")
|
||||
|
||||
if 0 < options.mcc > 999:
|
||||
parser.error("Invalid Mobile Country Code (MCC)")
|
||||
if 0 < options.mnc > 999:
|
||||
parser.error("Invalid Mobile Network Code (MNC)")
|
||||
|
||||
if options.smsp is not None:
|
||||
if not isnum(options.smsp):
|
||||
parser.error("Invalid SMSP Number")
|
||||
else:
|
||||
options.smsp = '00%d' % options.country + '5555'
|
||||
|
||||
return options
|
||||
|
||||
|
||||
def main():
|
||||
|
||||
# Parse options
|
||||
opts = parse_options()
|
||||
|
||||
# Load state
|
||||
sm = StateManager(opts.state_file, opts)
|
||||
sm.load()
|
||||
|
||||
# Instanciate generator
|
||||
np = sm.network
|
||||
cpg = CardParametersGenerator(np.cc, np.mcc, np.mnc, sm.get_secret())
|
||||
|
||||
# Generate cards
|
||||
imsis = set()
|
||||
cards = []
|
||||
while len(cards) < opts.count:
|
||||
# Next number
|
||||
i = sm.next_gen_num()
|
||||
|
||||
# Generate card number
|
||||
cp = cpg.generate(i)
|
||||
|
||||
# Check for dupes
|
||||
if cp.imsi in imsis:
|
||||
continue
|
||||
imsis.add(cp.imsi)
|
||||
|
||||
# Collect
|
||||
cards.append(cp)
|
||||
|
||||
# Save cards
|
||||
if opts.write_hlr:
|
||||
hlr_write_cards(opts.write_hlr, np, cards)
|
||||
|
||||
if opts.write_csv:
|
||||
csv_write_cards(opts.write_csv, np, cards)
|
||||
|
||||
# Save state
|
||||
sm.save()
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
Executable
+213
@@ -0,0 +1,213 @@
|
||||
#!/usr/bin/env python
|
||||
|
||||
#
|
||||
# Utility to write the cards
|
||||
#
|
||||
#
|
||||
# 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/>.
|
||||
#
|
||||
|
||||
from optparse import OptionParser
|
||||
|
||||
from pySim.commands import SimCardCommands
|
||||
from pySim.cards import _cards_classes
|
||||
|
||||
from ccc import StateManager, CardParameters
|
||||
|
||||
|
||||
|
||||
def csv_load_cards(filename):
|
||||
import csv
|
||||
fh = open(filename, 'r')
|
||||
cr = csv.reader(fh)
|
||||
cards = dict([(int(x[0]), CardParameters(int(x[0]), x[1], x[2], x[3])) for x in cr])
|
||||
fh.close()
|
||||
return cards
|
||||
|
||||
|
||||
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
|
||||
|
||||
|
||||
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
|
||||
""" % params
|
||||
|
||||
|
||||
#
|
||||
# Main
|
||||
#
|
||||
|
||||
def parse_options():
|
||||
|
||||
parser = OptionParser(usage="usage: %prog [options]")
|
||||
|
||||
# Card interface
|
||||
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,
|
||||
)
|
||||
|
||||
# Data source
|
||||
parser.add_option("--state", dest="state_file", metavar="FILE",
|
||||
help="Use this state file",
|
||||
)
|
||||
parser.add_option("--read-csv", dest="read_csv", metavar="FILE",
|
||||
help="Read parameters from CSV file",
|
||||
)
|
||||
|
||||
(options, args) = parser.parse_args()
|
||||
|
||||
if options.type == 'list':
|
||||
for kls in _cards_classes:
|
||||
print kls.name
|
||||
sys.exit(0)
|
||||
|
||||
if args:
|
||||
parser.error("Extraneous arguments")
|
||||
|
||||
return options
|
||||
|
||||
|
||||
def 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)
|
||||
|
||||
# Load state
|
||||
sm = StateManager(opts.state_file)
|
||||
sm.load()
|
||||
|
||||
np = sm.network
|
||||
|
||||
# Load cards
|
||||
cards = csv_load_cards(opts.read_csv)
|
||||
|
||||
# Iterate
|
||||
done = False
|
||||
first = True
|
||||
card = None
|
||||
|
||||
while not done:
|
||||
# Connect transport
|
||||
print "Insert card now (or CTRL-C to cancel)"
|
||||
sl.wait_for_card(newcardonly=not first)
|
||||
|
||||
# Not the first anymore !
|
||||
first = 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()
|
||||
|
||||
# Get parameters
|
||||
cp = cards[sm.next_write_num()]
|
||||
cpp = {
|
||||
'name': np.name,
|
||||
'smsp': np.smsp,
|
||||
'iccid': cp.iccid,
|
||||
'mcc': np.mcc,
|
||||
'mnc': np.mnc,
|
||||
'imsi': cp.imsi,
|
||||
'ki': cp.ki,
|
||||
}
|
||||
print_parameters(cpp)
|
||||
|
||||
# Program the card
|
||||
print "Programming ..."
|
||||
card.program(cpp)
|
||||
|
||||
# Update state
|
||||
sm.save()
|
||||
|
||||
# Done for this card and maybe for everything ?
|
||||
print "Card written !\n"
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
@@ -0,0 +1,193 @@
|
||||
#!/usr/bin/env python
|
||||
|
||||
#
|
||||
# CCC Event HLR management common stuff
|
||||
#
|
||||
#
|
||||
# Copyright (C) 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/>.
|
||||
#
|
||||
|
||||
import hashlib
|
||||
import os
|
||||
import random
|
||||
|
||||
from collections import namedtuple
|
||||
|
||||
try:
|
||||
import json
|
||||
except Importerror:
|
||||
# Python < 2.5
|
||||
import simplejson as json
|
||||
|
||||
#
|
||||
# Various helpers
|
||||
#
|
||||
|
||||
def isnum(s, l=-1):
|
||||
return s.isdigit() and ((l== -1) or (len(s) == l))
|
||||
|
||||
|
||||
#
|
||||
# Storage tuples
|
||||
#
|
||||
|
||||
CardParameters = namedtuple("CardParameters", "num iccid imsi ki")
|
||||
NetworkParameters = namedtuple("NetworkParameters", "name cc mcc mnc smsp")
|
||||
|
||||
|
||||
#
|
||||
# State management
|
||||
#
|
||||
|
||||
class StateManager(object):
|
||||
|
||||
def __init__(self, filename=None, options=None):
|
||||
# Filename for state storage
|
||||
self._filename = filename
|
||||
|
||||
# Params from options
|
||||
self._net_name = options.name if options else None
|
||||
self._net_cc = options.country if options else None
|
||||
self._net_mcc = options.mcc if options else None
|
||||
self._net_mnc = options.mnc if options else None
|
||||
self._net_smsp = options.smsp if options else None
|
||||
|
||||
self._secret = options.secret if options else None
|
||||
|
||||
# Default
|
||||
self._num_gen = 0
|
||||
self._num_write = 0
|
||||
|
||||
def load(self):
|
||||
# Skip if no state file
|
||||
if self._filename is None:
|
||||
return
|
||||
|
||||
# Skip if doesn't exist yet
|
||||
if not os.path.isfile(self._filename):
|
||||
return
|
||||
|
||||
# Read
|
||||
fh = open(self._filename, 'r')
|
||||
data = fh.read()
|
||||
fh.close()
|
||||
|
||||
# Decode json and merge
|
||||
dd = json.loads(data)
|
||||
|
||||
self._net_name = dd['name']
|
||||
self._net_cc = dd['cc']
|
||||
self._net_mcc = dd['mcc']
|
||||
self._net_mnc = dd['mnc']
|
||||
self._net_smsp = dd['smsp']
|
||||
self._secret = dd['secret']
|
||||
self._num_gen = dd['num_gen']
|
||||
self._num_write = dd['num_write']
|
||||
|
||||
def save(self):
|
||||
# Skip if no state file
|
||||
if self._filename is None:
|
||||
return
|
||||
|
||||
# Serialize
|
||||
data = json.dumps({
|
||||
'name': self._net_name,
|
||||
'cc': self._net_cc,
|
||||
'mcc': self._net_mcc,
|
||||
'mnc': self._net_mnc,
|
||||
'smsp': self._net_smsp,
|
||||
'secret': self._secret,
|
||||
'num_gen': self._num_gen,
|
||||
'num_write': self._num_write,
|
||||
})
|
||||
|
||||
# Save in json
|
||||
fh = open(self._filename, 'w')
|
||||
fh.write(data)
|
||||
fh.close()
|
||||
|
||||
@property
|
||||
def network(self):
|
||||
return NetworkParameters(
|
||||
self._net_name,
|
||||
self._net_cc,
|
||||
self._net_mcc,
|
||||
self._net_mnc,
|
||||
self._net_smsp,
|
||||
)
|
||||
|
||||
def get_secret(self):
|
||||
return self._secret
|
||||
|
||||
def next_gen_num(self):
|
||||
n = self._num_gen
|
||||
self._num_gen += 1
|
||||
return n
|
||||
|
||||
def next_write_num(self):
|
||||
n = self._num_write
|
||||
self._num_write += 1
|
||||
return n
|
||||
|
||||
#
|
||||
# Card parameters generation
|
||||
#
|
||||
|
||||
class CardParametersGenerator(object):
|
||||
|
||||
def __init__(self, cc, mcc, mnc, secret):
|
||||
# Digitize country code (2 or 3 digits)
|
||||
self._cc_digits = ('%03d' if cc > 100 else '%02d') % cc
|
||||
|
||||
# Digitize MCC/MNC (5 or 6 digits)
|
||||
self._plmn_digits = ('%03d%03d' if mnc > 100 else '%03d%02d') % (mcc, mnc)
|
||||
|
||||
# Store secret
|
||||
self._secret = secret
|
||||
|
||||
def _digits(self, usage, len_, num):
|
||||
s = hashlib.sha1(self._secret + usage + '%d' % num)
|
||||
d = ''.join(['%02d'%ord(x) for x in s.digest()])
|
||||
return d[0:len_]
|
||||
|
||||
def _gen_iccid(self, num):
|
||||
iccid = (
|
||||
'89' + # Common prefix (telecom)
|
||||
self._cc_digits + # Country Code on 2/3 digits
|
||||
self._plmn_digits # MCC/MNC on 5/6 digits
|
||||
)
|
||||
ml = 20 - len(iccid)
|
||||
iccid += self._digits('ccid', ml, num)
|
||||
return iccid
|
||||
|
||||
def _gen_imsi(self, num):
|
||||
ml = 15 - len(self._plmn_digits)
|
||||
msin = self._digits('imsi', ml, num)
|
||||
return (
|
||||
self._plmn_digits + # MCC/MNC on 5/6 digits
|
||||
msin # MSIN
|
||||
)
|
||||
|
||||
def _gen_ki(self):
|
||||
return ''.join(['%02x' % random.randrange(0,256) for i in range(16)])
|
||||
|
||||
def generate(self, num):
|
||||
return CardParameters(
|
||||
num,
|
||||
self._gen_iccid(num),
|
||||
self._gen_imsi(num),
|
||||
self._gen_ki(),
|
||||
)
|
||||
Executable
+480
@@ -0,0 +1,480 @@
|
||||
#!/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
|
||||
|
||||
|
||||
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("-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", "--smsp", dest="smsp",
|
||||
help="SMSP [default: '00 + country code + 5555']",
|
||||
)
|
||||
|
||||
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("-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",
|
||||
)
|
||||
|
||||
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",
|
||||
)
|
||||
|
||||
(options, args) = parser.parse_args()
|
||||
|
||||
if options.type == 'list':
|
||||
for kls in _cards_classes:
|
||||
print kls.name
|
||||
sys.exit(0)
|
||||
|
||||
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 ((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")
|
||||
|
||||
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 _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 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 (20 digits)
|
||||
if opts.iccid is not None:
|
||||
iccid = opts.iccid
|
||||
if not _isnum(iccid, 20):
|
||||
raise ValueError('ICCID must be 20 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 = 20 - 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)
|
||||
|
||||
# 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 _isnum(smsp):
|
||||
raise ValueError('SMSP must be digits only !')
|
||||
|
||||
else:
|
||||
smsp = '00%d' % opts.country + '5555' # Hack ...
|
||||
|
||||
# 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)])
|
||||
|
||||
# Return that
|
||||
return {
|
||||
'name' : opts.name,
|
||||
'iccid' : iccid,
|
||||
'mcc' : mcc,
|
||||
'mnc' : mnc,
|
||||
'imsi' : imsi,
|
||||
'smsp' : smsp,
|
||||
'ki' : ki,
|
||||
}
|
||||
|
||||
|
||||
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
|
||||
""" % params
|
||||
|
||||
|
||||
def write_parameters(opts, params):
|
||||
# CSV
|
||||
if opts.write_csv:
|
||||
import csv
|
||||
row = ['name', 'iccid', 'mcc', 'mnc', 'imsi', 'smsp', 'ki']
|
||||
f = open(opts.write_csv, 'a')
|
||||
cw = csv.writer(f)
|
||||
cw.writerow([params[x] for x in row])
|
||||
f.close()
|
||||
|
||||
# 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:]
|
||||
],
|
||||
)
|
||||
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()
|
||||
|
||||
|
||||
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:
|
||||
# Connect transport
|
||||
print "Insert card now (or CTRL-C to cancel)"
|
||||
sl.wait_for_card(newcardonly=not first)
|
||||
|
||||
# Not the first anymore !
|
||||
first = 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
|
||||
cp = gen_parameters(opts)
|
||||
print_parameters(cp)
|
||||
|
||||
# Program the card
|
||||
print "Programming ..."
|
||||
card.program(cp)
|
||||
|
||||
# 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
|
||||
|
||||
+259
@@ -0,0 +1,259 @@
|
||||
#!/usr/bin/env python
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
""" pySim: Card programmation logic
|
||||
"""
|
||||
|
||||
#
|
||||
# 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 pySim.utils import b2h, swap_nibbles, rpad, lpad
|
||||
|
||||
|
||||
class Card(object):
|
||||
|
||||
def __init__(self, scc):
|
||||
self._scc = scc
|
||||
|
||||
def _e_iccid(self, iccid):
|
||||
return swap_nibbles(iccid)
|
||||
|
||||
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
|
||||
24*'f' + 'fd' + 24*'f' + # 25b (unknown ...)
|
||||
rpad(p['smsp'], 20) + # 10b SMSP (padded with ff if needed)
|
||||
10*'f' # 5b (unknown ...)
|
||||
)
|
||||
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)
|
||||
|
||||
|
||||
# In order for autodetection ...
|
||||
_cards_classes = [ FakeMagicSim, SuperSim, MagicSim ]
|
||||
@@ -0,0 +1,95 @@
|
||||
#!/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(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(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('a02000' + ('%02x' % chv_no) + '08' + fc)
|
||||
@@ -0,0 +1,33 @@
|
||||
#!/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
|
||||
@@ -0,0 +1,88 @@
|
||||
#!/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
|
||||
@@ -0,0 +1,80 @@
|
||||
#!/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)
|
||||
@@ -0,0 +1,224 @@
|
||||
#!/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)
|
||||
@@ -0,0 +1,44 @@
|
||||
#!/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