Compare commits

..

8 Commits

Author SHA1 Message Date
Gerrit User 1000004 5e889e9506 Update patch set 2
Patch Set 2: Code-Review+1

Patch-set: 2
Reviewer: Gerrit User 1000004 <1000004@035e6965-6537-41bd-912c-053f3cf69326>
Label: Code-Review=+1, c061124251e8cd46c993d63a9611527dd2db85af
Attention: {"person_ident":"Gerrit User 1000004 \u003c1000004@035e6965-6537-41bd-912c-053f3cf69326\u003e","operation":"REMOVE","reason":"\u003cGERRIT_ACCOUNT_1000004\u003e replied on the change"}
2026-03-10 17:42:26 +00:00
Gerrit User 1000002 859abcbf7e Update patch set 2
Patch Set 2: Verified+1

5 passed:
* [distcheck] https://jenkins.osmocom.org/jenkins/job/gerrit-pysim-build/JOB_TYPE=distcheck,a1=default,a3=default,a4=default,label=osmocom-gerrit/2899/consoleFull
* [docs] https://jenkins.osmocom.org/jenkins/job/gerrit-pysim-build/JOB_TYPE=docs,a1=default,a3=default,a4=default,label=osmocom-gerrit/2899/consoleFull
* [lint] https://jenkins.osmocom.org/jenkins/job/gerrit-lint/27336/consoleFull
* [pylint] https://jenkins.osmocom.org/jenkins/job/gerrit-pysim-build/JOB_TYPE=pylint,a1=default,a3=default,a4=default,label=osmocom-gerrit/2899/consoleFull
* [test] https://jenkins.osmocom.org/jenkins/job/gerrit-pysim-build/JOB_TYPE=test,a1=default,a3=default,a4=default,label=simtester/2899/consoleFull

Build Successful

Patch-set: 2
Tag: jenkins
Reviewer: Gerrit User 1000002 <1000002@035e6965-6537-41bd-912c-053f3cf69326>
Label: Verified=+1, 6fe2f2d69e662f0a1c8d374fffd25fa1ac3aa519
2026-03-10 15:28:22 +00:00
Gerrit User 1000002 fe1b885de8 Update patch set 2
Patch Set 2:

Build Started
https://jenkins.osmocom.org/jenkins/job/gerrit-pysim/2904/consoleFull

Patch-set: 2
Tag: jenkins
2026-03-10 14:29:47 +00:00
Gerrit User 1000028 b8dda17f04 Create patch set 2
Uploaded patch set 2.

Outdated Votes:
* Code-Review+1 (copy condition: "changekind:NO_CHANGE OR changekind:TRIVIAL_REBASE OR is:MIN")
* Verified-1 (copy condition: "changekind:NO_CHANGE OR changekind:NO_CODE_CHANGE")


Patch-set: 2
Subject: PySimLogger: add parameter to set initial log-level/verbosity
Commit: dd2fb82cda
Tag: autogenerated:gerrit:newPatchSet
Groups: dd2fb82cda
Attention: {"person_ident":"Gerrit User 1000004 \u003c1000004@035e6965-6537-41bd-912c-053f3cf69326\u003e","operation":"ADD","reason":"Vote got outdated and was removed: Code-Review+1"}
2026-03-10 14:29:21 +00:00
Gerrit User 1000004 564f199c82 Update patch set 1
Patch Set 1: Code-Review+1

Patch-set: 1
Reviewer: Gerrit User 1000004 <1000004@035e6965-6537-41bd-912c-053f3cf69326>
Label: Code-Review=+1, 360de93749b6421882f6137d17a278c8ba7385fe
2026-03-10 12:22:04 +00:00
Gerrit User 1000002 76fd765b73 Update patch set 1
Patch Set 1: Verified-1

1 failed:
* [pylint] https://jenkins.osmocom.org/jenkins/job/gerrit-pysim-build/JOB_TYPE=pylint,a1=default,a3=default,a4=default,label=osmocom-gerrit/2893/consoleFull

4 passed:
* [distcheck] https://jenkins.osmocom.org/jenkins/job/gerrit-pysim-build/JOB_TYPE=distcheck,a1=default,a3=default,a4=default,label=osmocom-gerrit/2893/consoleFull
* [docs] https://jenkins.osmocom.org/jenkins/job/gerrit-pysim-build/JOB_TYPE=docs,a1=default,a3=default,a4=default,label=osmocom-gerrit/2893/consoleFull
* [lint] https://jenkins.osmocom.org/jenkins/job/gerrit-lint/27326/consoleFull
* [test] https://jenkins.osmocom.org/jenkins/job/gerrit-pysim-build/JOB_TYPE=test,a1=default,a3=default,a4=default,label=simtester/2893/consoleFull

Build Failed

Find the Retrigger button here:
https://jenkins.osmocom.org/jenkins/job/gerrit-pysim/2899/

Patch-set: 1
Tag: jenkins
Reviewer: Gerrit User 1000002 <1000002@035e6965-6537-41bd-912c-053f3cf69326>
Label: Verified=-1, 24ef1c526487d47e971a99a9569d1d5a425a376a
Attention: {"person_ident":"Gerrit User 1000028 \u003c1000028@035e6965-6537-41bd-912c-053f3cf69326\u003e","operation":"ADD","reason":"A robot voted negatively on a label"}
2026-03-10 11:58:33 +00:00
Gerrit User 1000002 d51585863d Update patch set 1
Patch Set 1:

Build Started
https://jenkins.osmocom.org/jenkins/job/gerrit-pysim/2899/consoleFull

Patch-set: 1
Tag: jenkins
CC: Gerrit User 1000002 <1000002@035e6965-6537-41bd-912c-053f3cf69326>
2026-03-10 11:39:59 +00:00
Gerrit User 1000028 a8b16ffa7d Create change
Uploaded patch set 1.

Patch-set: 1
Change-id: I4b8ef1e203186878910c9614a1d900d5759236a8
Subject: contrib/csv-to_pgsl: explicitly set log level to INFO in non verbose mode
Branch: refs/heads/master
Status: new
Topic: T0
Commit: c01abe29d5
Tag: autogenerated:gerrit:newPatchSet
Groups: c01abe29d5
Private: false
Work-in-progress: false
2026-03-10 11:39:21 +00:00
16 changed files with 0 additions and 2469 deletions
-2
View File
@@ -1,2 +0,0 @@
*.pyc
.*.swp
-339
View File
@@ -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.
-36
View File
@@ -1,36 +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)
# Print IMSI
print sc.read_binary(['3f00', '7f20', '6f07'])
# Run A3/A8
print sc.run_gsm('00112233445566778899aabbccddeeff')
-153
View File
@@ -1,153 +0,0 @@
#!/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()
-230
View File
@@ -1,230 +0,0 @@
#!/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()
-213
View File
@@ -1,213 +0,0 @@
#!/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()
-193
View File
@@ -1,193 +0,0 @@
#!/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(),
)
-480
View File
@@ -1,480 +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
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
View File
-259
View File
@@ -1,259 +0,0 @@
#!/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 ]
-95
View File
@@ -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(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)
-33
View File
@@ -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
-88
View File
@@ -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
-80
View File
@@ -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)
-224
View File
@@ -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)
-44
View File
@@ -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