Important!

Blog moved to https://blog.apdu.fr/

I moved my blog from https://ludovicrousseau.blogspot.com/ to https://blog.apdu.fr/ . Why? I wanted to move away from Blogger (owne...

Showing posts with label Python. Show all posts
Showing posts with label Python. Show all posts

Friday, April 14, 2023

Verify with OpenSSL a signature computed by PyKCS11

With PyKCS11 I provide a sample code signature.py to compute a RSA+SHA256 signature. The Python sample also contains the code to check the signature using PyKCS11.

But what if you want to verify the signature using OpenSSL?

Export the public key

PYKCS11LIB environment variable is used to indicate what PKCS#11 library to use. For the tests I use SoftHSM so I set the variable using:
$ export PYKCS11LIB=/usr/local/lib/softhsm/libsofthsm2.so
#!/bin/bash

set -e

# get the 1st key object ID
ID=$(pkcs11-tool --module $PYKCS11LIB --list-objects --type pubkey \
    | grep ID \
    | cut -d: -f 2)
echo "Object id: $ID"

# export the public key
pkcs11-tool --module $PYKCS11LIB --read-object --type pubkey --id $ID -o rsa_pub.key

# convert the public key to PEM
openssl rsa -pubin -inform DER -in rsa_pub.key -outform PEM -out rsa_pub.pem

The RSA key pair has been generated by the generate.py script and is stored in the PKCS#11 token. We need to export it so that OpenSSL can use it to check the signature.

To export the key I use pkcs11-tool from the OpenSC project. We need to know the object ID of the public key. This ID is configured in generate.py script line 22. We dump the public keys and get the object ID.

$ pkcs11-tool --module $PYKCS11LIB --list-objects --type pubkey
Using slot 0 with a present token (0x27ca3aa)
Public Key Object; RSA 1024 bits
  label:      My Public Key
  ID:         22
  Usage:      encrypt, verify, wrap
  Access:     local

The script will work correctly if only one public key is present in the token. I let you handle more complex cases.

output

$ ./export_key.sh 
Using slot 0 with a present token (0x27ca3aa)
Object id:          22
Using slot 0 with a present token (0x27ca3aa)
writing RSA key

Compute signature

I modified the original signature.py script to also save the clear text message in a file cleartext.txt and the signature in a file sig_sha256.bin so these files can be used later by OpenSSL.

#!/usr/bin/env python3

from PyKCS11 import *
import binascii

pkcs11 = PyKCS11Lib()
pkcs11.load()  # define environment variable PYKCS11LIB=YourPKCS11Lib

# get 1st slot
slot = pkcs11.getSlotList(tokenPresent=True)[0]

session = pkcs11.openSession(slot, CKF_SERIAL_SESSION | CKF_RW_SESSION)
session.login("1234")

# message to sign
toSign = "Hello World!\n"
mechanism = Mechanism(CKM_SHA256_RSA_PKCS, None)

# find first private key and compute signature
privKey = session.findObjects([(CKA_CLASS, CKO_PRIVATE_KEY)])[0]
signature = session.sign(privKey, toSign, mechanism)
print("\nsignature: {}".format(binascii.hexlify(bytearray(signature))))

# save the clear text in a file
with open("cleartext.txt", "w") as f:
    f.write(toSign)

# save to a signature in a file
with open("sig_sha256.bin", "bw") as f:
    f.write(bytearray(signature))

# find first public key and verify signature
pubKey = session.findObjects([(CKA_CLASS, CKO_PUBLIC_KEY)])[0]
result = session.verify(pubKey, toSign, signature, mechanism)
print("\nVerified:", result)

# logout
session.logout()
session.closeSession()

Output

$ ./signature.py 

signature: b'322c1591cb9aba1e361264b02464a2bd9d55693bf772b4253da0862616e611dc139005742c511795c27c8f609e4ddbaafceba1c3b3ce278b8e0af564c84de54a639cff67a9a3f97dcc542cd6f0200954ef7fce4a0f87b61636272e21fc1e3ef9f0b683e360cca4231405dd90ae2c4a3638ca7a85e2b62f6ae30975ff3885ab60'

Verified: True

Verify signature

#!/bin/bash

set -e

# verify signature
openssl dgst -sha256 -verify rsa_pub.pem -signature sig_sha256.bin cleartext.txt

Output

$ ./verify.sh 
Verified OK

Conclusion

Thanks to Leon Rman for the initial code and the idea.

I let you write the code to do the symmetrical operations: sign using OpenSSL and verify using PyKCS11.

Friday, March 31, 2023

PySCard 2.0.7 released

I just released a new version 2.0.7 of pyscard. PySCard is a python module adding smart cards support (PC/SC) to Python.

The PySCard project is available at:

 

Changes:

2.0.7 (March 2023)

  • do not include the generated HTML documentation in the archive


2.0.6 (March 2023)

  • handle SCARD_E_NO_SERVICE on Windows (on last reader removal)
  • add support of MSYS2/mingw environment on Windows
  • improve documentation
  • minor changes

Tuesday, December 27, 2022

PySCard (smartcard Python wrapper) API documentation update

The documentation for the PySCard API was not updated since 2017 (5 years ago).

epydoc → pydoctor

The tool used to generate the HTML documentation is epydoc. The latest epydoc version was released in 2008 (14 years ago). This tool is no more available in Debian so I had to move to something else.

The (new) tool to replace epydoc is pydoctor. It is actively maintained and generates nicer HTML pages.

For example, nice changes are:

Previous documentation


 

The old (epydoc) documentation is still available online at https://pyscard.sourceforge.io/epydoc/

New documentation



Conclusion

The new API documentation is available at https://pyscard.sourceforge.io/apidocs/index.html or from the project main documentation page https://pyscard.sourceforge.io/index.html

I guess I could improve the documentation. Feel free to report issues or suggest improvements.

Wednesday, September 28, 2022

PySCard 2.0.5 released

I just released a new version 2.0.5 of pyscard. PySCard is a python module adding smart cards support (PC/SC) to Python.

The PySCard project is available at:

This version is a not even a bug fix release. No code has changed.

The problem is that for the previous version, 2.0.4, I uploaded a incorrect source archive to Pypi. I inadvertently included some generated files in the .tar.gz archive. I discovered the problem while creating the Debian package. I removed the incorrect file from Pypi. But then it is not possible to upload a new file with a name that was already present on Pypi.

The source .tar.gz is already present in the pyscard project on sourceforge.net so I thought it was OK.

But then I received bug reports like Missing source release for 2.0.4? or pyscard 2.0.4 not available on linux from pypi so I had to do something.


Changes:

Sunday, August 28, 2022

PySCard 2.0.4 released

I just released a new version 2.0.4 of pyscard. PySCard is a python module adding smart cards support (PC/SC) to Python.

The PySCard project is available at:

This version is a bug fix release.

Changes:

2.0.4 (August 2022)
  • Fix a problem on PCSCCardConnection.disconnect
  • Add support of BaseSCardException(hresult) format
  • Do not use deprecated distutils anymore


Friday, March 18, 2022

PySCard 2.0.3 released

I just released a new version 2.0.3 of pyscard. PySCard is a python module adding smart cards support (PC/SC) to Python.

The PySCard project is available at:

This version is a bug fix release.

Changes:

  • PCSCExceptions: include error code in the message
  • getReaderNames(): fix Windows 10 issue when the last reader is disconnected

Monday, December 27, 2021

Windows PC/SC and SCARD_E_SERVICE_STOPPED (part 2)

In a previous blog article " Windows PC/SC and SCARD_E_SERVICE_STOPPED " I presented a problem on Windows when the last reader is disconnected.

After a discussion on the pcsclite-muscle mailing list I got nice feedback.Thanks.

Diego de Felice gave a reference to a Microsoft documentation from 2016 "What's New in Smart Cards"

Smart Card Service start and stop behavior

Smart card reader detection logic has been added so that the Smart Card Service runs only when appropriate. On Windows Server 2012 and Windows 8, the Smart Card Service (scardsvr) automatically starts when the user connects a smart card reader and automatically stops when a user removes a smart card reader and no other smart card reader is connected to the computer. On startup, the Smart Card Service automatically starts if a reader was previously connected to the computer but a reader is not currently connected to the system. If no smart card readers are connected to the computer, the service will automatically shut down one minute after the last API call into the Smart Card Service. If a reader was never previously connected to the computer, the service will not start automatically.

So the (strange) behavior is documented by Microsoft.

Stephan Brunner then gave a solution: establish a new context.


Source code

I wanted to test the hack proposed by Stephan.

#! /usr/bin/env python3

from smartcard.scard import *
from smartcard.pcsc.PCSCExceptions import *
import time

hresult, hcontext = SCardEstablishContext(SCARD_SCOPE_USER)
if hresult != SCARD_S_SUCCESS:
    raise EstablishContextException(hresult)

i = 0
while True:
    hresult, readers = SCardListReaders(hcontext, [])
    exc = BaseSCardException(message="", hresult=hresult)
    print('(%d) %s:' % (i, str(exc)), readers)
    if not hresult in [SCARD_S_SUCCESS, SCARD_E_NO_READERS_AVAILABLE]:
        hresult, hcontext = SCardEstablishContext(SCARD_SCOPE_USER)
        if hresult != SCARD_S_SUCCESS:
            raise EstablishContextException(hresult)

    time.sleep(1)
    i += 1

Output

(base) C:\Users\ludovic\Documents>python SCardListReaders.py
(0) : Impossible de trouver un lecteur de carte Å• puce.  (0x8010002E): []
(1) : Impossible de trouver un lecteur de carte Å• puce.  (0x8010002E): []
(2) : Impossible de trouver un lecteur de carte Å• puce.  (0x8010002E): []
(3) : Impossible de trouver un lecteur de carte Å• puce.  (0x8010002E): []
(4) : L’opération a réussi.  (0x00000000): ['Generic EMV Smartcard Reader 0']
(5) : L’opération a réussi.  (0x00000000): ['Generic EMV Smartcard Reader 0']
(6) : L’opération a réussi.  (0x00000000): ['Generic EMV Smartcard Reader 0']
(7) : L’opération a réussi.  (0x00000000): ['Generic EMV Smartcard Reader 0']
(8) : L’opération a réussi.  (0x00000000): ['Generic EMV Smartcard Reader 0']
(9) : Le gestionnaire de ressources des cartes Å• puce s’est arrÄ™té.  (0x8010001E): []
(10) : Impossible de trouver un lecteur de carte Å• puce.  (0x8010002E): []
(11) : Impossible de trouver un lecteur de carte Å• puce.  (0x8010002E): []
(12) : Impossible de trouver un lecteur de carte Å• puce.  (0x8010002E): []
(13) : L’opération a réussi.  (0x00000000): ['Generic EMV Smartcard Reader 0']
(14) : L’opération a réussi.  (0x00000000): ['Generic EMV Smartcard Reader 0']
(15) : L’opération a réussi.  (0x00000000): ['Generic EMV Smartcard Reader 0']
(16) : L’opération a réussi.  (0x00000000): ['Generic EMV Smartcard Reader 0']
(17) : Le gestionnaire de ressources des cartes Å• puce s’est arrÄ™té.  (0x8010001E): []
(18) : Impossible de trouver un lecteur de carte Å• puce.  (0x8010002E): []
(19) : Impossible de trouver un lecteur de carte Å• puce.  (0x8010002E): []

Description

Step 0, I start the script with no smart card reader connected. So SCardListReaders() returns the expected 0x8010002E which is SCARD_E_NO_READERS_AVAILABLE.

Step 4, Then I connect a smart card reader.

Step 9, Then I disconnect the smart card reader and SCardListReaders() returns the unexpected error 0x8010001E i.e. SCARD_E_SERVICE_STOPPED.

The program then calls SCardEstablishContext() again, with success, and continue.

Step 10, The next call to CardListReaders() returns the expected 0x8010002E. So I guess the PC/SC resource manager has been  re-started. 

Step 13, Connecting a new smart card reader works fine and the reader is detected.

Step 17, I disconnect the reader and I get again the SCARD_E_SERVICE_STOPPED error.

Without the hack

If the code does not establish a new context then CardListReaders() will return SCARD_E_SERVICE_STOPPED for ever. And if I connect a new reader it is not detected.

I think this behavior explains the bug reported in CardRequest waitforcard ATR can't see reconnected readers on Windows (regression) #123.


Conclusion

Ignoring SCARD_E_SERVICE_STOPPED, as I did in commit 5b43ef5, is not a correct solution. I have to work on a better solution (just for Windows).

Sunday, December 26, 2021

Windows PC/SC and SCARD_E_SERVICE_STOPPED

It looks like Windows introduced a new behavior when the last smart card reader is disconnected. The PC/SC layer (WinSCard library) will return the error SCARD_E_SERVICE_STOPPED.

According to MSDN "Smart Card Return Values":

SCARD_E_SERVICE_STOPPED
0x8010001E
The smart card resource manager has shut down. 

 

Demonstration

The idea is to wait for a smart card reader if none is already present.

Then wait for the card reader to be disconnected.

Once disconnected SCardListReaders() will return SCARD_E_SERVICE_STOPPED (0x8010001E) instead of the expected SCARD_E_NO_READERS_AVAILABLE (0x8010002E).


Source code

#! /usr/bin/env python3

from smartcard.scard import *
from smartcard.pcsc.PCSCExceptions import *
import time

hresult, hcontext = SCardEstablishContext(SCARD_SCOPE_USER)
if hresult != SCARD_S_SUCCESS:
    raise EstablishContextException(hresult)

hresult, readers = SCardListReaders(hcontext, [])
if not hresult in [SCARD_S_SUCCESS, SCARD_E_NO_READERS_AVAILABLE]:
    raise ListReadersException(hresult)
print('PC/SC Readers:', readers)

if not len(readers):
    print("Wait for the first reader")
    while not len(readers):
        hresult, readers = SCardListReaders(hcontext, [])
        if not hresult in [SCARD_S_SUCCESS, SCARD_E_NO_READERS_AVAILABLE]:
            raise ListReadersException(hresult)
        time.sleep(1)
    print('PC/SC Readers:', readers)

print("wait for the last reader removal")
while len(readers):
    hresult, readers = SCardListReaders(hcontext, [])
    if not hresult in [SCARD_S_SUCCESS, SCARD_E_NO_READERS_AVAILABLE]:
        raise ListReadersException(hresult)
    time.sleep(1)
print('PC/SC Readers:', readers)

hresult = SCardReleaseContext(hcontext)
if hresult != SCARD_S_SUCCESS:
    raise ReleaseContextException(hresult)

GNU/Linux result

rousseau@debian:~ $ ./SCardListReaders.py
PC/SC Readers: []
Wait for the first reader
PC/SC Readers: ['Alcor Micro AU9540 00 00']
wait for the last reader removal
PC/SC Readers: []

Windows result

(base) C:\Users\ludovic\Documents>python SCardListReaders.py
PC/SC Readers: []
Wait for the first reader
PC/SC Readers: ['Generic EMV Smartcard Reader 0']
wait for the last reader removal
Traceback (most recent call last):
  File "C:\Users\ludovic\Documents\SCardListReaders.py", line 29, in <module>
    raise ListReadersException(hresult)
smartcard.pcsc.PCSCExceptions.ListReadersException: Failed to list readers:
 Le gestionnaire de ressources des cartes Å• puce s’est arrÄ™té. (0x8010001E)

Sorry for the error message in French. I don't know an equivalent of "LANG=C program" to have program run and use the default language (English).

You can also note the strange display of accented letters. Windows is not yet Unicode ready? :-)


Problems

The problem is that this new behavior broke code in PySCard, my PC/SC Python wrapper.

For example see

Yes, it is an old issue. I tried to fix it many times but the fixes has some other side effects and the problem is still present somewhere.

Windows is NOT my preferred platform (far from it) so fixing Windows issue is a P.I.T.A. for me. Help is really welcome here.

 

But Why?

What problem Microsoft is trying to solve with this behavior?

Why killing the resource manager and returning an error while there is still PC/SC clients connected to the resource manager?

If you have an idea of the answers please tell me.
If you know a way to change the behavior please tell me.

Use the pcsclite-muscle mailing list to discuss this issue.

Wednesday, September 22, 2021

PySCard 2.0.2 released

I just released a new version 2.0.2 of pyscard. PySCard is a python module adding smart cards support (PC/SC) to Python.

The PySCard project is available at:

This version is a bug fix release.

Changes:

  • Fix an issue in ReaderMonitor() that failed to enumerate readers on macOS
  • getReaderNames(): handle SCARD_E_SERVICE_STOPPED on Windows generated on last reader removal
  • Restrict MAX_BUFFER_SIZE_EXTENDED to 65535 and fix an issue on Lenovo laptops with NXP NFC readers
  • SmartcardException: display a positive PCSC error code

Thursday, June 17, 2021

PySCard 2.0.1 released

I just released a new version 2.0.1 of pyscard. PySCard is a python module adding smart cards support (PC/SC) to Python.

The PySCard project is available at:

 

Changes:

 2.0.1 (June 2021)

  • Add .reconnect() method to high-level API
  • Handle removal of the last reader on Windows
  • Fix support of macOS Big Sur
  • Use Python3 by default
  • Handle bogus ACS ACR122U PICC reader

Wednesday, February 24, 2021

Writing a SIM card phone book in Python

In the previous article "Reading a SIM card phone book in Python" I presented a program to read the phone book from a SIM card. I will now present the writing part.

 

Source code

The source code is in 2 parts: usim.py and usim_write.py files.

usim.py is the exact same file as in the previous article.

usim_write.py is:

#!/usr/bin/env python3

from smartcard.util import toBytes, padd
import random
import usim


def get_name():
    # List of firstnames from https://www.data.gouv.fr/fr/datasets/liste-de-prenoms/
    with open(random.choice(['f', 'm'])) as fd:
        lines = list(map(str.strip, fd.readlines()))
    return " ".join([random.choice(lines), random.choice(lines),
                    random.choice(lines)])


def get_number():
    numbers = "0123456789"
    phone = list()
    phone.append(random.choice(numbers) + random.choice(numbers))
    phone.append(random.choice(numbers) + random.choice(numbers))
    phone.append(random.choice(numbers) + random.choice(numbers))
    phone.append(random.choice(numbers) + random.choice(numbers))
    phone.append(random.choice(numbers) + random.choice(numbers))
    return " ".join(phone)


def new_record(size):
    # size-14 characters for the name
    name = get_name()[0:size-14]
    phone = get_number()
    print("name:", name)
    print("phone:", phone)
    record = padd(list(map(ord, name)), size-14) \
        + padd(toBytes("06 A1 " + phone), 14)
    return record


def usim_write(reader_nb):
    # Select the EF ADN
    (size, connection) = usim.usim(reader_nb)

    for nbr in range(1, 250):
        record = new_record(size)
        #  Update record
        header = [0xA0, 0xDC]
        record_idx = nbr
        cmd = header + [record_idx, 0x04, size] + record
        data, sw1, sw2 = connection.transmit(cmd)
        if (sw1, sw2) != (0x90, 0x00):
            return


if __name__ == "__main__":
    import sys
    if 2 == len(sys.argv):
        reader_nb = int(sys.argv[1])
    else:
        reader_nb = 0
    usim_write(reader_nb)


Comments

I wanted to have reasonable names and phone numbers in my phone book. So I generate names by randomly selecting 3 first names from 2 lists: the 50 first (by frequency of use) male first names in French stored in the m file, and the 50 first female first names in French stored in the f file. I got the data from https://www.data.gouv.fr/fr/datasets/liste-de-prenoms/. The original list also contains first names from other languages.

The first 10 lines of m are:

Pierre
Juste
Julien
Olivier
Henri
Jacques
Philippe
Nicolas
Aime
Antoine
[...]

The first 10 lines of f are:

Marie
Victoire
Claire
Marine
Reine
Virginie
Vienne
Solange
Jolie
Marguerite
[...]

For the phone number I just select a random 10-digits number.

Maybe the entries I created are non functional in a real phone. First check it works for you if you want to reuse this code. Also if you plan to reuse my source code you must read "My blog source code license" first.


Output

$ ./usim_write.py 
Available readers:
- Gemalto PC Twin Reader
Using: Gemalto PC Twin Reader
connecting to Gemalto PC Twin Reader
Select MF
> A0 A4 00 00 02 3F 00
<  [] 9F 22
Select DF Telecom
> A0 A4 00 00 02 7F 10
<  [] 9F 22
Select EF ADN
> A0 A4 00 00 02 6F 3A
<  [] 9F 0F
Get Response
> A0 C0 00 00 0F
< 00 00 21 34 6F 3A 04 00 11 FF 22 01 02 01 22 90 00
name: Juliette Claire Fran
phone: 50 47 00 17 44
> A0 DC 01 04 22 4A 75 6C 69 65 74 74 65 20 43 6C 61 69 72 65 20 46 72 61 6E 06 A1 50 47 00 17 44 FF FF FF FF FF FF FF
<  [] 90 00
name: Luc Nicolas Regis
phone: 83 46 67 10 73
> A0 DC 02 04 22 4C 75 63 20 4E 69 63 6F 6C 61 73 20 52 65 67 69 73 FF FF FF 06 A1 83 46 67 10 73 FF FF FF FF FF FF FF
<  [] 90 00
name: Julien Jeremie Serge
phone: 89 07 17 20 07
> A0 DC 03 04 22 4A 75 6C 69 65 6E 20 4A 65 72 65 6D 69 65 20 53 65 72 67 65 06 A1 89 07 17 20 07 FF FF FF FF FF FF FF
<  [] 90 00
[...]

The output is truncated. I do not want to include all the 255 phone numbers.

If I use usim_read.py (with debug disabled) I get:

$ ./usim_read.py 
Available readers:
- Gemalto PC Twin Reader
Using: Gemalto PC Twin Reader
Select MF
Select DF Telecom
Select EF ADN
Get Response
1: Name: Juliette Claire Fra, phone: 0574007144
2: Name: Luc Nicolas Regis.., phone: 3864760137
3: Name: Julien Jeremie Serg, phone: 9870710270
[...]

Note that the phone numbers are reversed by group of 2 digits. 50 47 00 17 44 becomes 0574007144.


Conclusion

It is as easy to write than to read a SIM phone book.

My goal here was to be able to write "realistic" phone book entries so that the usim_read.py has "real" data to read and display.

usim_read.py will be used again in the next episode.

Monday, February 22, 2021

Reading a SIM card phone book in Python

I already wrote a SIM phone book dumper program in 2004. This first version was in Perl and was presented in "SIM card phone book listing".

You can also find more advanced tools in articles with the SIM label, like cardpeek, monosim or PSSI.

I now present a version in Python using the PySCard wrapper

 

Source code

The source code is in 2 parts: usim.py and usim_read.py files.

usim.py is:

#!/usr/bin/env python3

from smartcard.System import readers
from smartcard.util import toBytes
from smartcard.CardConnectionObserver import ConsoleCardConnectionObserver

debug = True

def usim(reader_nb):
    # get all the available readers
    r = readers()
    print("Available readers:")
    for reader in r:
        print("-", reader)

    reader = r[reader_nb]
    print("Using:", reader)

    connection = reader.createConnection()
    if debug:
        observer = ConsoleCardConnectionObserver()
        connection.addObserver(observer)
    connection.connect()

    SELECT = "A0 A4 00 00 02 "
    GET_RESPONSE = "A0 C0 00 00 "

    # Select MF
    print("Select MF")
    data, sw1, sw2 = connection.transmit(toBytes(SELECT + "3F 00"))
    if sw1 != 0x9F:
        raise(Exception("Error"))

    # Select DF Telecom
    print("Select DF Telecom")
    data, sw1, sw2 = connection.transmit(toBytes(SELECT + "7F 10"))
    if sw1 != 0x9F:
        raise(Exception("Error"))

    # Select EF ADN
    print("Select EF ADN")
    data, sw1, sw2 = connection.transmit(toBytes(SELECT + "6F 3A"))
    if (sw1, sw2) != (0x9F, 0x0F):
        raise(Exception("Error"))

    # Get Response
    print("Get Response")
    data, sw1, sw2 = connection.transmit(toBytes(GET_RESPONSE) + [sw2])
    if (sw1, sw2) != (0x90, 0x00):
        raise(Exception("Error"))

    size = data[-1]

    pin = None
    if pin:
        print(pin)
        pin = list(map(ord, pin))
        padd(pin, 8)

        # Verify CHV
        VERIFY = "A0 20 00 01 08"
        cmd = toBytes(VERIFY) + pin
        data, sw1, sw2 = connection.transmit(cmd)
        if (sw1, sw2) != (0x90, 0x00):
            raise(Exception("Wrong PIN:" + pin))

    return size, connection


if __name__ == "__main__":
    usim(0)

usim_read.py is:

#!/usr/bin/env python3

from smartcard.util import toBytes, toASCIIString
import usim


def decode_record(record):
    """
    decode_record(toBytes("43 75 73 74 6F 6D 65 72 20 43 61 72 65 FF 06 A1 80 00 07 70 00 FF FF FF FF FF FF FF"))
    >> ['Customer Care', '0800700700']
    """
    X = len(record) - 14
    name = toASCIIString(record[0:X - 1]).replace("ÿ", "")
    # number of bytes for the phone number
    tel_size = record[X]
    phone = record[X + 2:X + tel_size + 1]

    decoded = ""
    for n in phone:
        hex = "%02X" % n
        high = hex[0]
        low = hex[1]
        decoded += low + high
    # if the number of digits is odd we suppress the padding
    if decoded[-1] == "F":
        decoded = decoded[:-1]
    phone = decoded

    return name, phone


def usim_read(reader_nb):
    # Select the EF ADN
    (size, connection) = usim.usim(reader_nb)

    for nbr in range(1, 250):
        #  Read record
        header = [0xA0, 0xB2]
        record_idx = nbr
        cmd = header + [record_idx, 0x04, size]
        data, sw1, sw2 = connection.transmit(cmd)
        if (sw1, sw2) != (0x90, 0x00):
            return

        name, phone = decode_record(data)
        if name != "":
            print(f"{record_idx}: Name: {name}, phone: {phone}")


if __name__ == "__main__":
    import sys
    if 2 == len(sys.argv):
        reader_nb = int(sys.argv[1])
    else:
        reader_nb = 0
    usim_read(reader_nb)


Comments

The debug is enabled. So you can see the communication between the application and the card.
You ned to change only one line to remove the APDU log if needed. 

The phone book record size is not fixed for all the SIM cards. The record size is returned in the last byte of the GET RESPONSE command after the SELECT EF (Elementary File) ADN (Abbreviated dialling numbers).

You can get more details about the EF ADN and the record coding in the document ETSI TS 131 102 V16.6.0 (2021-01) chapter "4.4.2.3 EF_ADN (Abbreviated dialling numbers)". It is important to note that ETSI (European Telecommunications Standards Institute) standards are public and free.

By default the first PC/SC reader is used. But you can select another reader by passing a number as argument to the program. We will use this feature later.

No PIN is defined and verified. I am using a sysmocom sysmoUSIM-SJS1 card. This card has the user PIN disabled by default.
You can enable PIN verification by defining a PIN in usim.py. The code is already present.

USIM (Universal Subscriber Identity Module) is a new application introduced for GSM version 3 (3G) to replace/improve the SIM (subscriber identity/identification) application. The code should work the same with a SIM card or a USIM card (but untested).

Output

$ ./usim_read.py 
Available readers:
- Gemalto PC Twin Reader
Using: Gemalto PC Twin Reader
connecting to Gemalto PC Twin Reader
Select MF
> A0 A4 00 00 02 3F 00
<  [] 9F 22
Select DF Telecom
> A0 A4 00 00 02 7F 10
<  [] 9F 22
Select EF ADN
> A0 A4 00 00 02 6F 3A
<  [] 9F 0F
Get Response
> A0 C0 00 00 0F
< 00 00 21 34 6F 3A 04 00 11 FF 22 01 02 01 22 90 00
> A0 B2 01 04 22
< 4C 61 75 72 65 20 46 72 61 6E E7 6F 69 73 65 20 59 76 6F 6E 06 A1 24 66 85 10 22 FF FF FF FF FF FF FF 90 00
1: Name: Laure Francoise Yvo, phone: 4266580122
> A0 B2 02 04 22
< 4C 75 63 69 65 6E 6E 65 20 48 65 6C 65 6E 65 20 4C 75 63 69 06 A1 76 45 65 28 77 FF FF FF FF FF FF FF 90 00
2: Name: Lucienne Helene Luc, phone: 6754568277
> A0 B2 03 04 22
< 55 72 62 61 69 6E 20 47 69 6C 6C 65 73 20 4A 75 73 74 65 FF 06 A1 80 38 74 16 54 FF FF FF FF FF FF FF 90 00
3: Name: Urbain Gilles Juste, phone: 0883476145
> A0 B2 04 04 22
< 4A 65 72 6F 6D 65 20 50 61 73 63 61 6C 20 46 65 72 6E 61 6E 06 A1 80 53 42 10 86 FF FF FF FF FF FF FF 90 00
4: Name: Jerome Pascal Ferna, phone: 0835240168
[...]

The output is truncated. I do not want to include all the 255 phone numbers. 

Note that the names and numbers are random. More on that later.


Conclusion

It is easy to dump the phone book from a SIM card.

The SIM phone book is very limited (no birthday, no email address). The real phone book is, in general, in the phone itself and synchronised using CardDav with a server like Nextcloud.

Wednesday, September 16, 2020

PySCard 2.0.0 released

 I just released a new version 2.0.0 of pyscard. PySCard is a python module adding smart cards support (PC/SC) to Python.

The version is 2.0.0 because after 1.9.9 I had not so many choices. This version does not bring any new feature. It is a bug fix release.


The PySCard project is available at:


Changes

2.0.0 (September 2020)

  • SCardStatus(): Fix a crash in case of PC/SC error
  • toASCIIString(): replace non-ASCII characters by '.'
  • remove i386 (32-bits) support on macOS

Friday, July 31, 2020

New PyKCS11 1.5.9 available

I just released a new version of PyKCS11, a Python wrapper above the PKCS#11 API.
See "PyKCS11 introduction" or "PyKCS11’s documentation".

The project is registered at Pypi: https://pypi.org/project/PyKCS11/

Changes

1.5.9 - July 2020, Ludovic Rousseau
  • call C_GetSlotList() with a NULL parameter to correctly initialize some PKCS#11 lib conforming to PKCS#11 version 2.40.

Friday, May 15, 2020

New PyKCS11 1.5.8 available

I just released a new version of PyKCS11, a Python wrapper above the PKCS#11 API.
See "PyKCS11 introduction" or "PyKCS11’s documentation".

The project is registered at Pypi: https://pypi.org/project/PyKCS11/

Changes:

1.5.8 - May 2020, Ludovic Rousseau
  • CKA_ALWAYS_AUTHENTICATE is boolean
  • CKM_VENDOR_DEFINED_...
    • Fix name: use CKM_ instead of CKR_ prefix
    • Use an explicit hex prefix: CKM_VENDOR_DEFINED_0x45
  • Add missing CKM_*, CKA_*, CKF_*, CKD_*, CKK_*, CKN_*, CKO_*, CKR_* from PKCS#11 v3.0
  • fix test_asymetric.py for RSA_PSS_Mechanism

Wednesday, December 18, 2019

New PyKCS11 1.5.7 available

I just released a new version of PyKCS11, a Python wrapper above the PKCS#11 API.
See "PyKCS11 introduction" or "PyKCS11’s documentation".

Changes:

1.5.7 - December 2019, Ludovic Rousseau
  • add missing files in the .tar.gz

1.5.6 - December 2019, Ludovic Rousseau
  • AppVeyor:
    • generate bdist_wheel
    • add Python 3.7 and 3.8
  • Sample events.py: add a -f/--full argument
  • Add support of CKM_AES_GCM mechanism
  • CPKCS11Lib::Load(): return different error codes
  • minor improvements

Friday, August 9, 2019

PySCard 1.9.9 released

I just released a new version 1.9.9 of pyscard. PySCard is a python module adding smart cards support (PC/SC) to Python.

The PySCard project is available at:


Changes

1.9.9 (August 2019)
  • Makefile: use twine to upload to pypi.python.org
  • test: fix Exception test on 32-bits CPU
  • test: correctly handle macOS versions older than 10.10

Wednesday, April 17, 2019

New PyKCS11 1.5.5 available

I just released a new version of PyKCS11, a Python wrapper above the PKCS#11 API.
See "PyKCS11 introduction" or "PyKCS11’s documentation".

Changes:

1.5.5 - April 2019, Ludovic Rousseau
  • fix source package generation

1.5.4 - April 2019, Ludovic Rousseau
  • getTokenInfo: replace NUL char by ' ' in utcTime
  • dumpit:
    • print hardwareVersion and firmwareVersion
    • print slot flags
    • move to next slot if token not present
  • add support of CKA_WRAP_TEMPLATE/CKA_UNWRAP_TEMPLATE
  • add samples for ECC key generation and use
  • move from distutils to setuptools
  • upload of wheels to pypi on "make dist"

Monday, March 25, 2019

PySCard 1.9.8 released

I just released a new version 1.9.8 of pyscard. PySCard is a python module adding smart cards support (PC/SC) to Python.

The PySCard project is available at:

Changes

1.9.8 (March 2018)

  • SmartcardException: store the PC/SC return code in hresult
  • CardMonitoring: stop the looping only if PCSC exited
  • setup: support build on OpenBSD, and other BSD
  • Fix Windows 10 SCARD_E_SERVICE_STOPPED from SCardListReaders()
  • Minor documentation improvements

Friday, October 12, 2018

New PyKCS11 1.5.3 available

I just released a new version of PyKCS11, a Python wrapper above the PKCS#11 API.
See "PyKCS11 introduction" or "PyKCS11’s documentation".

Changes:

1.5.3 - October 2018, Ludovic Rousseau
  • Do not fail when converting a UTF-8 string
  • Documentation: convert from Epydoc to Sphinx
  • some minor improvements