Operating Systems


Introduction

Dans le cadre de ce travail pratique nous étions amené à effectuer une prise en main de divers aspects cryptographiques d’une télécommunication définie dans le standard GSM à travers COMP128

Qu’est-ce qu’est COMP128 ?

COMP128 est une implémentation des algorithmes A3 et A8 définis dans le standard GSM. Du fait que le standard ne définit pas d’implémentation unique de ces algorithmes, la responsabilité de l’implémentation de ceux-ci est laissée aux opérateurs

”[…] The operation of these functions falls completely within the domain of an individual operator, and therefore, the functions are specified by each operator rather than being fully standardised. […] — RFC 4186 Section 12.1

A3 est utilisé pour l’authentification de la station mobile (MS), quant à A8, il est utilisé pour générer une clé de session qui sera utilisé pour chiffrer la communication.

Dérivation de la clé de session

Le principe de base du chiffrement des communications GSM est que la station mobile ainsi que l’opérateur ont déjà “négocié” au préalable un secret partagé. La clé KiK_{i} de 16 bytes est présente dans la carte SIM de la station mobile ainsi que du côté du centre d’authentification de l’opérateur. Cette clé permettra en premier lieu d’authentifier la station mobile puis de dériver la clé de session pour le chiffrement des données.

Schéma de COMP128 --- Cours de M. Tewfiq Maliki

La première étape de l’algorithme est l’envoie d’un nombre aléatoire de 16 bytes à la station mobile. En théorie, la combinaison de la clé KiK_{i} avec le nombre aléatoire en entrée de l’algorithme A3 est censé produire une réponse signée (signed response SRES) de 4 bytes. La comparaison des valeurs obtenues du côté de la station mobile ainsi que du côte de la station de base (opérateur) permet dans le cas d’une valeur identique d’authentifier le client et de passer à l’étape suivante.

En second lieu, KiK_{i} ainsi que SRES seront fournis à l’algorithme A8 afin de dériver une clé de session KcK_{c} de 8 bytes.

Finalement, KcK_{c} sera utilisé comme clé de chiffrement par l’algorithme A5 afin de protéger la communication entre la station mobile et la station de base.

Prise en main

Du fait que COMP128 n’est pas un algorithme public et d’autant plus que les implémentations de A3 et de A8 n’étaient pas standardisées, il est impossible de se baser sur une source fiable et véridique de ces algoritmes (d’autant plus que le lien fourni dans “l’énoncé” n’est pas fonctionnel).

Par conséquent voici les sources utilisées pour ce travail pratique :

  • LokYan/comp128

    • Exemple de rétro-ingénierie des algorithmes A3 et A8 constituant COMP128.
  • Blowfish

    • Algorithme de chiffrement symétrique utilisé pour le chiffrement des données à la place d’A5 car son implémentation n’est pas disponible.

Envoi / Réception du nombre aléatoire

Le serveur s’occupe d’envoyer un nombre généré aléatoirement grâce à un générateur sécurisé (cryptographiquement parlant). Pour faire ceci, nous utilisons la librairie secrets.

rand = secrets.token_bytes(16)
print(
    f"Sending random number ({rand.hex()})to client for authentication")
conn.send(rnd)

Du côté du client, après avoir établi une connexion TCP avec le serveur, nous attendons de recevoir de sa part le nombre généré aléatoirement qui représente le challenge pour la station mobile.

rand = s.recv(16)
print(f"Random number received from server = {rand.hex()}")

Calcul de SRES et KcK_{c}

# Computing SRES and Kc
argv = ["./comp128_orig", args.key, "0x" + rand.hex()]
stdout = subprocess.run(
    argv, capture_output=True).stdout.decode("ascii")

values = stdout.split(" ")

SRES, KC = "0x" + values[0], "0x" + values[1]

En ayant obtenu celui-ci, nous créeons un sous-processus écrit en C qui va s’occuper de calculer SRES et KcK_{c} à partir de notre secret partagé KiK_{i} et le nombre aléatoire.

Du côté du serveur, le procédé est identique car il nous est nécessaire d’aboutir à la même signature SRES afin d’authentifier le client ce que nous faisons avec le bout de code ci-dessous.

print(f"Client = {sres_client_str} Ours = {SRES}")

if sres_client.decode("ascii") != SRES:
    print("Authentication has failed, terminating connection...")
    conn.close()
    os.exit(3)

print(f"{addr} you've been successfully authenticated!")

Chiffrement des données avec Blowfish

Après que le client est passé la phase d’authentification, il peut à présent envoyer ces données (sous la forme d’un fichier au serveur). Pour cette partie le choix d’algorithme était laissé libre, par conséquent nous avons choisi “Blowfish” qui est un algorithme de chiffrement symétrique par blocs développé par le fameux Bruce Schneier. Une des raisons pour lesquelles nous l’avons séléctionné est que cet algorithme est très flexible en terme de taille de clé ce qui nous est crucial afin d’émuler le comportement d’A5 qui fonctionne avec une clé de 8 bytes (64 bits).

Ci-dessous, voici l’extrait de code du côté du client qui s’occupe de chiffrer le contenu du fichier passé en argument au script.

cipher = blowfish.Cipher(str(args.key).encode())

with open(args.file, "rb") as file:
    while True:
        chunk = file.read(8)
        if len(chunk) == 0:
            break
        elif len(chunk) % 8 != 0:
            chunk += b' ' * (8 - len(chunk) % 8)
        encrypted_chunk = cipher.encrypt_block(chunk)
        s.send(encrypted_chunk)

Conclusion

Ce travail pratique nous a permis de prendre en main, la manière dont fonctionnait le chiffrement des données lors d’une communication GSM ainsi que d’émuler ce comportement à l’aide d’un modèle client-serveur écrit en python.

exdx\int_{-\infty}^{\infty} e^{x} \text{d}x