Using hexadecimal ADM1_PIN when flashing a SIM card

Hi all.

I’m hoping this is the right forum, and the question hasn’t been asked before.

I’m currently writing an applet for use in SIM cards.

Until now, we’ve been using an old home-brew Python tool to do the flashing. It works with 0348 encryption and a “custom” encryption (not sure how it works TBH).

We’ve come across a batch of SIM cards that we’re not able to flash, as our tool effectively behaves as an OTA platform, and sends PDUs that mimic an OTA campaign to flash a SIM card. The reason we can’t flash them is the cards seem to not have an OTA server running on them.

I’ve tried using GPPro, but apparently that tool doesn’t support SCP80 or 81, and I just can’t get it working.

I’ve had way more luck with pySIM-shell however. (as in I’ve spent 1% of the time I’ve spent struggling with GPPro, and I’ve got 10x further in that time).

I’ve got a live SIM card, made by Idemia. We’ve got ADM1 and ADM2 (plus KIC/KIK/KID 1,2,3 and F).
I want to use this as the reference, as we can flash this using our tool.

I start pySIM-shell using

python -p 0 -A BE4444FA5E94527C

It results in …

Using reader PCSC[Alcorlink USB Smart Card Reader 0]
Waiting for card...
Info: Card is of type: UICC
Detected UICC Add-on "SIM"
AIDs on card:
 USIM: a0000000871002ff49ffff89050e00ff (EF.DIR)
Welcome to pySim-shell!
(C) 2021-2023 by Harald Welte, sysmocom - s.f.m.c. GmbH and contributors
Online manual available at

All perfect.

If I use the ADM2 I have for this SIM card, it tells me that

Failed to verify chv_no 0x0A with code 0xF6D48A367D280982, 9 tries left.

So I know the ADM1_PIN as Hex via the CLI is working.

I can move round the file structure, get card info, and that’s all great.

When I come to verify the ADM, this is where stuff starts to go wrong.

I can’t specify the ADM PIN.

If I just try to verify on its own

pySIM-shell (00:MF)> verify_adm
EXCEPTION of type 'ValueError' occurred with message: cannot find ADM-PIN for ICCID '89xxxxxxxxxxxxxxxxxx'

If I specify the ADM_PIN I’ve already specified in the command line

pySIM-shell (00:MF)> verify_adm BE4444FA5E94527C
EXCEPTION of type 'ValueError' occurred with message: PIN-ADM needs to be <=8 digits (ascii)

The help for pySIM-shell says…

  -a PIN_ADM1,     --pin-adm PIN_ADM1
                        ADM PIN used for provisioning (overwrites default) (default: None)
  -A PIN_ADM1_HEX, --pin-adm-hex PIN_ADM1_HEX
                        ADM PIN used for provisioning, as hex string (16 characters long) (default: None)

To my eyes, PIN_ADM1 and PIN_ADM1_HEX are the same PIN, but one is in hex and the other is “raw”.

I’ve tried specifying the PIN as non-hex in the CLI…

python -p 0 -a 0xBE4444FA5E94527C

but it blows up with

ValueError: PIN-ADM needs to be <=8 digits (ascii)

Is there a workround? Is it a known issue? Am I being an idiot and have missed something in 6 foot tall letters in the docs?

I’m going to open pySIM-shell up and have a look to see what it’s doing, but I thought I’d ask here as well.

Many thanks.


I don’t think you’re doing anything wrong. It’s just likely the case that all the othe rpeople working on (and with) pySim during the last decade or so are using sysmocom cards, which only use decimal ADM1-pins. I do recall that in prehistoric days some other cards (was it Fairwaves?) used non-decimal ADM1 and we did add support for that. But it might have been broken during the many code refactorings etc. ever since.

Patches are always welcome!

so the first step of validating the verify_adm command argument is the pySim.utils.is_hexstr_or_decimal method. That method seems to correctly detect a hex-string as you don’t get an error message about its exceptions.

The problem is

        if opts.ADM1:
            # use specified ADM-PIN
            pin_adm = sanitize_pin_adm(opts.ADM1)
            iccid =['ICCID']
            # try to find an ADM-PIN if none is specified
            result = card_key_provider_get_field('ADM1', key='ICCID', value=iccid)
            pin_adm = sanitize_pin_adm(result)

where we always pass the opts.ADM1 (the use rinput) as the first argument to sanitize_pin_adm:

def sanitize_pin_adm(pin_adm, pin_adm_hex=None) -> Hexstr:
    The ADM pin can be supplied either in its hexadecimal form or as
    ascii string. This function checks the supplied opts parameter and
    returns the pin_adm as hex encoded string, regardless in which form
    it was originally supplied by the user

    if pin_adm is not None:
        if len(pin_adm) <= 8:
            pin_adm = ''.join(['%02x' % (ord(x)) for x in pin_adm])
            pin_adm = rpad(pin_adm, 16)

            raise ValueError("PIN-ADM needs to be <=8 digits (ascii)")

    if pin_adm_hex is not None:
        if len(pin_adm_hex) == 16:
            pin_adm = pin_adm_hex
            # Ensure that it's hex-encoded
                try_encode = h2b(pin_adm)
            except ValueError as exc:
                raise ValueError("PIN-ADM needs to be hex encoded using this option") from exc
            raise ValueError("PIN-ADM needs to be exactly 16 digits (hex encoded)")

    return pin_adm

So as can be seen above, we actually would have to pass a hex-encoded ADM1 PIN as second argument. The point is, the caller site doesn’t really know. And it’s a bi hard to reliably guess, after all something like 12345678 could either be

  • an 8-digit decimal code which we need to translate to 3132333435363738 hex before sending it to the card, or
  • an 8-digit hexadecimal (4-byte) code that transpareltly needs to be passed to the card

So the real solutoin is likely to add support for something like a 0x prefix. So if the user specifies 12345678 it’s decimal, and if the user specifies 0x12345678 it’s hexadecimal.

Would you be interested in working on a related patch?

As this is a bug report, I created a bug ticket at Bug #6480: pySim-shell support for hexadecimal ADM PIN is broken - pySim - Open Source Mobile Communications were it would b ebetter to discuss this further.


In principle I’m not opposed to working on a bug.

The santitise_pin_adm takes 2 parameters, the binary ADM key and an optional ADM_HEX key.

For now, I’ve hacked it by passing in a hard-coded ADM1_HEX on line 747 of

It’s been a few years since I’ve programmed Python in anger, so it’ll take me a while to get back into it.

If I can find a way of pulling the provided ADM1_HEX key in at that point, I’ll pass it in, and it should be OK.

I’m in a bit of a sticky situation at present in that we’ve received 2 batches of SIM cards from our supplier.

The first set (about 10 of them) will not accept new applets when authenticated with the ADM1 key, but will accept applets when we provide the KIC and KID and go in via 0348.

These cards were good, so we ordered a batch of 100, and these seem to have some kind of problem.

Our home-brew flashing tool (at least 10 years old) will flash the old cards, but any attempt to use 0348 results in 6D00.

I’ve tried using GPPro, but I’ve never managed to get it to do anything at all on any of the cards I’ve ever tried. What I have managed to ascertain is that the old ones don’t support quite a few of the global platform commands (mostly in the GET_DATA area).

More distressing is that the new cards immediately fail with GPPro because

Could not SELECT Security Domain: 0x6A82 (Application/file not found)

I’m wondering what they’ve sent us!

Using pySIM-shell I can navigate around the file system, read the contents of files, get card info etc. It just works!

One thing I can’t do is get access to the Global Platform Commands. I was expecting that by running verify_adm that it would wake up the commands and I could do some stuff.

I’m running it, but nothing happens other than telling me the ADM1_KEY is good.

What I’ve seen is that if I select a file, a new set of options appear in the “help” screen. I need to find out how these are added and removed, then find out how the Global Platform commands are added/removed, and try to figure out the trigger for that.

Again, have I missed something?

As I said above, it’s been a few years since I’ve used Python to this extent, so it’ll take me a while to find my feet.

Once I’ve looked further, I can try taking a look at that bug.



Sorry, one more question.

We’ve been provided with a KIC, KID and KIK.

I’ve realised I can select “the applet” by selecting file a0000000871002.

I then get a new menu.

I’ve tried authenticating, but I know know how.

The params are rand and autn.

I’m assuming rand is just a random number. What is autn? I’ve tried using the KIC,KID and KIK, and none of those work.



As an aside, I’ve got “a fix” for the verify_adm, but it’s a dirty hack (checking the length, and if it’s 16, trying the ADM key as a string and if it’s not, trying it as a binary).

I don’t like that though, but providing an 0x prefix breaks the parser as it’s expecting a hex, i.e. no “0x”. I’ll find a fix shortly, then do a commit.

Is there a do/don’t commit rule for commit messages? On our system the commit message must contain the ticket ID. Is that what’s done around here, or is a clean description enough?


you’d need to replace the is_hexstr_or_decimal function to accomodate for the 0x syntax.

The general patch workflow is described at Gerrit - Cellular Network Infrastructure - Open Source Mobile Communications - but I think we didn’t document the “Closes: OS#xxxx” for issues there. Just do a quick “git log” and you will see some examples of that.