# coding: utf-8
# # Vérification de numéros de Cartes Bleues, RIB (IBAN) et NIRPP (sécu)
#
# Ce petit notebook va implémenter les algorithmes de vérification des numéros de :
#
# - cartes bleues, sur $4\times4$ chiffres, avec un chiffre de vérification,
# - RIB (identifiant de compte, sur les IBAN), avec deux chiffres de vérifications,
# - NIRPP (numéro de sécurité sociale en France), avec deux chiffres de vérifications.
#
# > J'avais déjà implementé les deux derniers, cf. ces scripts : [check_IBAN.py](https://bitbucket.org/lbesson/bin/src/master/check_IBAN.py), et [check_NIRPP.py](https://bitbucket.org/lbesson/bin/src/master/check_NIRPP.py).
#
# Si vous êtes curieux de l'aspect historique, [ce petit article](https://spectrum.ieee.org/tech-history/silicon-revolution/hans-peter-luhn-and-the-birth-of-the-hashing-algorithm) explique très bien les origines de ces chiffres de contrôles et de la [formule de Luhn](https://fr.wikipedia.org/wiki/Formule_de_Luhn).
# Je vais utiliser cette fonction plusieurs fois, qui permet de transformer une lettre 'A',...,'Z' en entier.
# In[1]:
def l_to_c(l):
try:
return str(int(l))
except ValueError:
return str(10 + ord(l.upper()) - ord('A'))
# In[2]:
for l in 'ABCDEFGHIJKLMNROPQRSTUVWXYZ':
print("l = {} --> c = {}".format(l, l_to_c(l)))
# ## Cartes Bleues
#
# - Références : https://www.hellolife.fr/article/a-quoi-correspondent-les-chiffres-sur-ma-carte-bancaire_a466/1 et https://fr.wikipedia.org/wiki/Carte_de_paiement#Num%C3%A9ro_de_carte_bancaire et https://fr.wikipedia.org/wiki/Formule_de_Luhn
# - Exemple :
#
# In[3]:
exemple_cb = "4970 1012 3456 7890" # pas valide
# D'abord l'algorithme de Luhn :
# In[4]:
def verifie_Luhn(numeros):
numeros = numeros.replace(' ', '')
nb_chiffres = len(numeros)
parite = nb_chiffres % 2
chiffres = [int(l_to_c(l)) for l in numeros]
somme = chiffres[nb_chiffres - 1]
for i in range(nb_chiffres - 2, -1, -1):
chiffre = chiffres[i]
if i % 2 == parite:
chiffre *= 2
if chiffre > 9:
chiffre -= 9
somme += chiffre
return (somme % 10) == 0
# In[5]:
verifie_Luhn('972 487 086')
# In[6]:
verifie_Luhn('972 487 081')
verifie_Luhn('972 487 082')
verifie_Luhn('972 487 087')
# In[7]:
verifie_Luhn(exemple_cb)
# Ensuite la vérification pour un numéro de carte bleue :
# In[8]:
def verifie_cb(cb):
print("\nVérification du numéro de CB '%s'..." % cb)
check = verifie_Luhn(cb)
if check:
print("OK '%s' semble être un numéro de CB valide." % cb)
else:
print("[ATTENTION] PAS OK '%s' semble ne pas être un numéro de CB valide!" % cb)
return check
# ### Exemples
# In[9]:
verifie_cb(exemple_cb)
# Avec un autre faux numéro mais conçu pour être vrai :
# In[10]:
verifie_cb("4976 5301 7218 3533")
# ## RIB/IBAN
#
# - Référence : https://fr.wikipedia.org/wiki/International_Bank_Account_Number#Algorithme_de_v.C3.A9rification_de_l.27IBAN
# - Exemple :
#
# In[11]:
exemple_iban = "FR76 1254 8029 9838 3759 0150 071"
# In[12]:
def verifie_iban(iban):
print("\nVérification du nombre IBAN '%s'..." % iban)
ib = iban.replace(' ', '')
ib = ib[4:] + ib[:4]
print(" De longueur", len(ib))
i = int(''.join(l_to_c(l) for l in ib))
check = (i % 97) == 1
if check:
print("OK '%s' semble être un nombre IBAN valide." % iban)
else:
print("[ATTENTION] PAS OK '%s' semble ne pas être un nombre IBAN valide!" % iban)
return check
# ### Exemples
# #### Compte français
# In[13]:
verifie_iban(exemple_iban)
# #### Compte anglais
# In[14]:
verifie_iban("GB87 BARC 2065 8244 9716 55")
# In[15]:
verifie_iban("GB87 BARC 2065 8244 9716 51")
# #### Compte belge
# In[16]:
verifie_iban("BE43 0689 9999 9501")
# In[17]:
verifie_iban("BE43 0689 9999 9500")
# ## NIRPP
# - Référence : https://fr.wikipedia.org/wiki/Num%C3%A9ro_de_s%C3%A9curit%C3%A9_sociale_en_France#cite_note-8
# - Exemple :
#
# In[18]:
exemple_nirpp = "2 69 05 49 588 157 80"
# In[19]:
length_checksum = 2
def verifie_nirpp(nirpp, length_checksum=length_checksum):
print("\nVérification du nombre NIRPP '%s' ..." % nirpp)
ib = nirpp.replace(' ', '')
checksum = int(ib[-length_checksum:])
ib = ib[:-length_checksum]
print(" De longueur", len(ib))
num_nirpp = int(''.join(l_to_c(l) for l in ib))
print(" De somme de contrôle num_nirpp =", num_nirpp)
print(" Module à 97 =", (97 - (num_nirpp % 97)))
print(" Et la somme de contrôle attendue était", checksum)
check = (97 - (num_nirpp % 97)) == checksum
if check:
print("OK '%s' semble être un nombre NIRPP valide." % nirpp)
else:
print("[ATTENTION] PAS OK '%s' semble ne pas être un nombre NIRPP valide!" % nirpp)
return check
# ### Exemples
# In[20]:
verifie_nirpp(exemple_nirpp)
# ### Bonus : affichage d'un NIRPP
# - Référence : https://fr.wikipedia.org/wiki/Num%C3%A9ro_de_s%C3%A9curit%C3%A9_sociale_en_France#Signification_des_chiffres_du_NIR
#
# Il suffit de récupérer les informations de chaque morceau du code NIRPP, et les stocker comme ça :
# In[21]:
information_nirpp = {
(0, 1): {
"meaning": "sexe",
"mapping": {
"1": "homme",
"2": "femme",
"3": "personne étrangère de sexe masculin en cours d'immatriculation en France",
"4": "personne étrangère de sexe féminin en cours d'immatriculation en France"
}
},
(1, 2): {
"meaning": "deux derniers chiffres de l'année de naissance",
"mapping": {
# DONE nothing to do for this information
}
},
(3, 2): {
"meaning": "mois de naissance",
"mapping": {
"01": "janvier",
"02": "février",
"03": "mars",
"04": "avril",
"05": "mai",
"06": "juin",
"07": "juillet",
"08": "août",
"09": "septembre",
"10": "octobre",
"11": "novembre",
"12": "décembre",
}
},
# Only case A : TODO implement case B and C
(5, 2): {
"meaning": "département de naissance métropolitain",
"mapping": { # Cf. http://www.insee.fr/fr/methodes/nomenclatures/cog/documentation.asp
"01": "Ain",
"02": "Aisne",
"03": "Allier",
"04": "Alpes-de-Haute-Provence",
"05": "Hautes-Alpes",
"06": "Alpes-Maritimes",
"07": "Ardèche",
"08": "Ardennes",
"09": "Ariège",
"10": "Aube",
"11": "Aude",
"12": "Aveyron",
"13": "Bouches-du-Rhône",
"14": "Calvados",
"15": "Cantal",
"16": "Charente",
"17": "Charente-Maritime",
"18": "Cher",
"19": "Corrèze",
"2A": "Corse-du-Sud",
"2B": "Haute-Corse",
"21": "Côte-d'Or",
"22": "Côtes-d'Armor",
"23": "Creuse",
"24": "Dordogne",
"25": "Doubs",
"26": "Drôme",
"27": "Eure",
"28": "Eure-et-Loir",
"29": "Finistère",
"30": "Gard",
"31": "Haute-Garonne",
"32": "Gers",
"33": "Gironde",
"34": "Hérault",
"35": "Ille-et-Vilaine",
"36": "Indre",
"37": "Indre-et-Loire",
"38": "Isère",
"39": "Jura",
"40": "Landes",
"41": "Loir-et-Cher",
"42": "Loire",
"43": "Haute-Loire",
"44": "Loire-Atlantique",
"45": "Loiret",
"46": "Lot",
"47": "Lot-et-Garonne",
"48": "Lozère",
"49": "Maine-et-Loire",
"50": "Manche",
"51": "Marne",
"52": "Haute-Marne",
"53": "Mayenne",
"54": "Meurthe-et-Moselle",
"55": "Meuse",
"56": "Morbihan",
"57": "Moselle",
"58": "Nièvre",
"59": "Nord",
"60": "Oise",
"61": "Orne",
"62": "Pas-de-Calais",
"63": "Puy-de-Dôme",
"64": "Pyrénées-Atlantiques",
"65": "Hautes-Pyrénées",
"66": "Pyrénées-Orientales",
"67": "Bas-Rhin",
"68": "Haut-Rhin",
"69": "Rhône",
"70": "Haute-Saône",
"71": "Saône-et-Loire",
"72": "Sarthe",
"73": "Savoie",
"74": "Haute-Savoie",
"75": "Paris",
"76": "Seine-Maritime",
"77": "Seine-et-Marne",
"78": "Yvelines",
"79": "Deux-Sèvres",
"80": "Somme",
"81": "Tarn",
"82": "Tarn-et-Garonne",
"83": "Var",
"84": "Vaucluse",
"85": "Vendée",
"86": "Vienne",
"87": "Haute-Vienne",
"88": "Vosges",
"89": "Yonne",
"90": "Territoire de Belfort",
"91": "Essonne",
"92": "Hauts-de-Seine",
"93": "Seine-Saint-Denis",
"94": "Val-de-Marne",
"95": "Val-d'Oise",
# TODO support these too
"971": "Guadeloupe",
"972": "Martinique",
"973": "Guyane",
"974": "La Réunion",
"975": "Saint-Pierre-et-Miquelon",
"976": "Mayotte",
"977": "Saint-Barthélemy",
"978": "Saint-Martin",
"984": "Terres australes et antarctiques françaises",
"986": "Wallis-et-Futuna",
"987": "Polynésie française",
"988": "Nouvelle-Calédonie",
"989": "Île de Clipperton"
}
},
(7, 3): {
"meaning": "code officiel de la commune de naissance",
"mapping": { # TODO
}
},
(10, 3): {
"meaning": "numéro d’ordre de la naissance dans le mois et la commune (ou le pays)",
"mapping": {
# DONE nothing to do for this information
}
}
}
# Pour les villes, on a besoin d'une base de donnée plus grande. J'ai récupéré [ce fichier](https://bitbucket.org/lbesson/bin/src/master/comsimp2016.txt) sur le site de l'INSEE (lien mort).
# In[22]:
get_ipython().system('ls data/')
# In[23]:
get_ipython().system('wc data/comsimp2016.txt')
# Il ressemble à ça :
# In[24]:
get_ipython().system('head data/comsimp2016.txt')
# Briançon est bien dans la liste :
# In[25]:
get_ipython().system('grep "BRIANCON" data/comsimp2016.txt')
# Allons-y :
# In[26]:
import subprocess
length_checksum = 2
def pprint_nirpp(nirpp, length_checksum=length_checksum):
print("\nAffichage d'informations contenues dans le numéro NIRPP '%s' ..." % nirpp)
nirpp = nirpp.replace(' ', '')
ib = nirpp[:-length_checksum]
# Printing
for (i, l) in sorted(information_nirpp):
n = nirpp[i: i + l]
info = information_nirpp[(i, l)]
if n in info["mapping"]:
explain = "\"{}\"".format(info["mapping"][n])
else:
explain = n
# For towns, durty hack to extract the town from the INSEE database
if i == 7:
try:
args = [
"grep", "--", "',{},{},'".format(
nirpp[5: 5 + 2],
nirpp[7: 7 + 3]
),
"data/comsimp2016.txt",
"|", "cut", "-d,", "-f10"
]
command = ' '.join(args)
# print("Executing subprocess.check_output to \"{}\"".format(command))
explain = subprocess.check_output(command, shell=True)
explain = explain[:-1].decode()
# print("explain =", explain)
explain = "{} (code {})".format(explain, nirpp[7: 7 + 3])
except Exception as e:
# print("e =", e)
explain = n
print("- Le nombre '{}' (indice {}:{}) signifie:\n\t\"{}\" : \t{}".format(
n, i, i + l, info["meaning"], explain)
)
# In[27]:
pprint_nirpp(exemple_nirpp)
# Avec un exemple assez proche de mon numéro de sécurité sociale (modifié) :
# In[28]:
pprint_nirpp("1 93 01 05 023 122 23")
# ## IMEI
# Les numéros d'identification des téléphones portables ([les IMEI](https://fr.wikipedia.org/wiki/International_Mobile_Equipment_Identity#Structure)) terminent aussi par un chiffre de contrôle, qui utilise aussi la [formule de Luhn](https://fr.wikipedia.org/wiki/Formule_de_Luhn).
# Je termine ce notebook en implémentant aussi cette vérification.
#
#
# In[29]:
exemple_imei = "448674 52 897641 0" # avant 2014, 6-2-6-1
# In[30]:
def verifie_imei(imei):
print("\nVérification du numéro IMEI '%s'..." % imei)
check = verifie_Luhn(imei)
if check:
print("OK '%s' semble être un numéro IMEI valide." % imei)
else:
print("[ATTENTION] PAS OK '%s' semble ne pas être un numéro IMEI valide!" % imei)
return check
# In[32]:
verifie_imei(exemple_imei)
# ### Exemples
# In[33]:
exemple_imei = "448674 52 897641 1"
# In[34]:
verifie_imei(exemple_imei)
# In[35]:
exemple_imei = "468674 52 897641 0"
# In[36]:
verifie_imei(exemple_imei)
# Avec un IMEI semblable à celui d'un de mes anciens téléphones :
# In[39]:
mon_faux_imei_1 = "35569508 262195 2"
verifie_imei(mon_faux_imei_1)
# In[40]:
mon_faux_imei_2 = "35569508 283295 5"
verifie_imei(mon_faux_imei_2)
# ## Conclusion
#
# Voilà, c'est tout pour aujourd'hui !
#
# > Allez lire [ici pour voir mes autres notebooks](https://GitHub.com/Naereen/notebooks).