from IPython.display import Image
Image('m94.jpg')
[ Cette image est empruntée au site de Bob Lord qui contient plusieurs images du M-94. ]
Le cryptographe M-94 (CSP-488 pour l'US Navy) est un cryptographe Bazeries à 25 rondelles en aluminium, chaque rondelle portant un alphabet de 26 lettres.
Basé sur les travaux préliminaires du Colonel Parker Hitt en 1911 (Hitt n'a connu le travail de Bazeries qu'à l'automne de 1914) et mis au point par le Major Joseph Mauborgne, ce cryptographe a été adopté par l'armée américaine en 1922. Il est resté en usage jusqu'en 1945.
Une variante, le M-138, appelée chiffrement à bandes [strip cipher], reprenait les 25 alphabets du M-94 déroulés, mis à plat et répétés deux fois sur des bandes métalliques. Ces bandes métalliques, numérotées comme les disques du M-94, peuvent coulisser dans un cadre. Les bandes étant placées dans l'ordre de la clé, on forme le message à gauche de la règle (5) en faisant coulisser les bandes. En faisant glisser la règle (4), on sélectionne une génératrice pour le texte chiffré, comme pour le M-94. Les deux cryptographes sont compatibles.
Une variante ultérieure, le M-138-A, comportera 30 bandes à choisir parmi 100, ce qui accroit beaucoup la sécurité du système.
Image('m138.jpg')
L'idée n'est pas nouvelle. Dès 1893, Arthur Hermann avait inventé un cryptographe à réglettes construit d'après le système de Bazeries.
Le manuel d'utilisation du M-94 montre que son fonctionnement est identique à celui du cryptographe de Bazeries.
On peut donc réutiliser les fonctions développées dans l'étude de ce dernier. Voir Cryptographe de Bazeries.
Les 25 alphabets sont incohérents et non pas déduits de mots-clés comme pour le cryptographe de Bazeries (sauf le dix-septième alphabet)
alpha = [
'ABCEIGDJFVUYMHTQKZOLRXSPWN',
'ACDEHFIJKTLMOUVYGZNPQXRWSB',
'ADKOMJUBGEPHSCZINXFYQRTVWL',
'AEDCBIFGJHLKMRUOQVPTNWYXZS',
'AFNQUKDOPITJBRHCYSLWEMZVXG',
'AGPOCIXLURNDYZHWBJSQFKVMET',
'AHXJEZBNIKPVROGSYDULCFMQTW',
'AIHPJOBWKCVFZLQERYNSUMGTDX',
'AJDSKQOIVTZEFHGYUNLPMBXWCR',
'AKELBDFJGHONMTPRQSVZUXYWIC',
'ALTMSXVQPNOHUWDIZYCGKRFBEJ',
'AMNFLHQGCUJTBYPZKXISRDVEWO',
'ANCJILDHBMKGXUZTSWQYVORPFE',
'AODWPKJVIUQHZCTXBLEGNYRSMF',
'APBVHIYKSGUENTCXOWFQDRLJZM',
'AQJNUBTGIMWZRVLXCSHDEOKFPY',
'ARMYOFTHEUSZJXDPCWGQIBKLNV', # <-- ARMY OF THE US
'ASDMCNEQBOZPLGVJRKYTFUIWXH',
'ATOJYLFXNGWHVCMIRBSEKUPDZQ',
'AUTRZXQLYIOVBPESNHJWMDGFCK',
'AVNKHRGOXEYBFSJMUDQCLZWTIP',
'AWVSFDLIEBHKNRJQZGMXPUCOTY',
'AXKWREVDTUFOYHMLSIQNJCPGBZ',
'AYJPXMVKBQWUGLOSTECHNZFRID',
'AZDNBUHYFWJLVGRCQMPSOEXTKI'
]
La formation de la clé numérique à partir d'une phrase-clé est identique à celle du cryptographe de Bazeries.
def strkey2numkey(s):
"""Retourne la clé-liste qui correspond à la clé-chaine s
"""
key = s.split() # conversion de la clé en liste
for i in range(len(key)):
if key[i] != '?':
key[i] = int(key[i])-1 # index début = 0
return key
def numkey2strkey(k):
"""Retourne la clé-chaine qui correspond à la clé-liste k
"""
s = ''
for e in k:
if e == '?':
s += ' ?'
else:
s += str(' {:2d}'.format(e+1)) # index début = 1
return s
import unicodedata
def keyword2strkey(s):
"""Retourne la clé-chaine de longueur 25 correspondant au mot-clé s
"""
size = 25 # le cryptographe M94 a 25 rondelles
alpha26 = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' # alphabet complet
s = s.upper() # passage en majuscule
s = s.replace('Œ', 'OE') # au cas où...
s = unicodedata.normalize('NFKD', s)
s = u''.join([c for c in s if not unicodedata.combining(c)]) # suppression des accents
s = ''.join([c for c in s if c in alpha26]) # on ne garde que les lettres
if s == '':
return []
else:
s = s*(1+size//len(s))
s = s[:size] # mise à longueur de la clé alphabétique
k = [0]*size # initialisation clé-liste
idx = 0 # la clé-liste k comprend les entiers de 0 à 19
for c in alpha26: # pour chaque lettre de l'alphabet
i = s.find(c, 0) # on cherche si elle est dans le mot-clé
while i != -1:
k[i] = idx
idx += 1
i = s.find(c, i+1)
return numkey2strkey(k)
Exemple tiré du manuel : avec la phrase-clé GENERAL ELECTRIC COMPANY on obtient la clé numérique
11 6 17 7 22 1 14 8 15 9 3 24 23 13 4 5 20 16 21 2 18 25 12 10 19
Vérification :
keyword2strkey('GENERAL ELECTRIC COMPANY')
import random
def C_M94(clair, clef):
""" Chiffre le texte 'clair' avec le cryptographe M94 pour la clé fournie.
"""
nb_alpha = len(alpha) # nombre d'alphabets c-à-d de disques disponibles
nb_car = len(alpha[0]) # nombre de caractères par alphabet
k = strkey2numkey(clef)
key_len = len(k) # nombre de disques utilisés
n = len(clair)//key_len
segments = [clair[i*key_len:(i+1)*key_len] for i in range(n)]
if len(clair)%key_len:
segments.append(clair[n*key_len:])
crypto = ''
for s in segments:
idx = [0]*len(s)
g = random.randint(1, nb_car-1)
for i in range(len(s)):
idx[i] = alpha[k[i]].find(s[i])
crypto += ''.join([alpha[k[i]][(idx[i]+g)%nb_car] for i in range(len(s))])
return crypto
Prenons comme texte clair un passage tiré (au hasard !) de l'introduction du livre Machine Cryptography and Modern Cryptanalysis de Cipher A. Deavour et Louis Kruh :
In reading this book consider that the authors are both Americans and have a built-in bias about some subjects which may, at times, seem unfair. This is not intentional on our part, but we know of no way to eliminate the problem.
txt = 'INREADINGTHISBOOKCONSIDERTHATTHEAUTHORSAREBOTHAMERICANSANDHAVEABUILTINBIASABOUTSOME\
SUBJECTSWHICHMAYATTIMESSEEMUNFAIRTHISISNOTINTENTIONALONOURPARTBUTWEKNOWOFNOWAYTOELIMINATE\
THEPROBLEM'
clef = keyword2strkey('GENERAL ELECTRIC COMPANY')
crypto = C_M94(txt, clef)
crypto, len(crypto)
from math import log10
logf4g ={} # dic des log des fréquences des 4-grammes
f = open('brut4g_en.txt') # statistique des quadrigrammes anglais
total = 0 # effectif total
for line in f:
(w, c) = line.split(sep= ' ')
logf4g[w] = int(c)
total += int(c)
for w in logf4g:
logf4g[w] = -log10(logf4g[w]/total)
f.close()
def logscore(s):
logsum = 0
default = 100 # quadrigramme inconnu f = 10^-100
for i in range(len(s)-3):
logsum += logf4g.get(s[i:i+4], default)
return logsum
logscore('ALLISWELL')
logscore('ALILWSELL')
def D_M94(crypto, clef):
""" Déchiffre le texte 'crypto' avec le cryptographe M-94 pour la clé fournie.
La fonction logscore doit être définie pour la langue du texte clair.
"""
nb_alpha = len(alpha) # nombre d'alphabets c-à-d de disques disponibles
nb_car = len(alpha[0]) # nombre de caractères par alphabet
k = strkey2numkey(clef)
key_len = len(k) # nombre de disques utilisés sur le cryptographe
n = len(crypto)//key_len
segments = [crypto[i*key_len:(i+1)*key_len] for i in range(n)]
if len(crypto)%key_len:
segments.append(crypto[n*key_len:])
clair = ''
segment_precedent = ''
for s in segments:
idx = [0]*len(s)
best_score = float('inf')
best_txt = ''
for g in range(nb_car):
for i in range(len(s)):
idx[i] = alpha[k[i]].find(s[i])
txt = ''.join([alpha[k[i]][(idx[i]+g)%nb_car] for i in range(len(s))])
score = logscore(segment_precedent+txt)
if score < best_score:
best_score = score
best_txt = txt
segment_precedent = best_txt
clair += best_txt
return clair
D_M94(crypto, clef)
On reprend l'attaque du cryptographe de Bazeries. Il suffit de changer quelques paramètres.
import itertools
def cherche_minimum(crypto, pos, key_len=25, nb_top=25):
""" Retourne la liste des nb_top meilleures 4-listes de la clé
à partir de la position pos donnée.
La longueur de la clé est de 25 pour le cryptographe M94.
"""
nb_alpha = len(alpha) # nombre d'alphabets c-à-d de disques disponibles
nb_car = len(alpha[0]) # nombre de caractères par alphabet
resultats = [] # liste des solutions
max_liste = 100*nb_top # limite avant purge de la liste de résultats
n = len(crypto)//key_len # nombre de segments complets de 20 caractères
segments = [crypto[i*key_len:(i+1)*key_len] for i in range(n)] # liste des segments
nb_disques = 4 # on cherche les 4-listes
list_idx = [i for i in range(nb_alpha)] # liste desnombres de 1 à 20
perms = itertools.permutations(list_idx, nb_disques)
for p in perms: # pour chaque 4-liste de [1..20]
best_score_p = 0 # meilleur score pour la 4-liste p
best_gs = [] # meilleures génératrices
for s in segments: # pour chaque segment s de 20 caractères du crypto
idx = [alpha[p[i]].find(s[i+pos]) for i in range(nb_disques)]
best_score_s = float('inf') # meilleur score pour le segment s
best_g = 0 # meilleure génératrice
for g in range(nb_car): # pour chaque génératrice
txt = ''.join([alpha[p[i]][(idx[i]+g)%nb_car] for i in range(nb_disques)])
score = logscore(txt)
if score < best_score_s: # sélection meilleure génératrice du segment s
best_score_s = score
best_g = g
best_score_p += best_score_s # m-à-j meilleur score pour la 4-liste
best_gs.append(best_g) # ajout de la meilleure génératrice
resultats.append([best_score_p, p, best_gs])
if len(resultats) > max_liste: # on limite la taille de la liste
resultats.sort(key=lambda tup: tup[0]) # classement
resultats = resultats[:nb_top] # on garde les nb_top meilleurs
resultats.sort(key=lambda tup: tup[0]) # classement
resultats = resultats[:nb_top] # on garde les nb_top meilleurs
resultats = [[pos, r[1], r[2]] for r in resultats] # on supprime le logscore
return resultats
cherche_minimum(crypto, 0)
def diff_listes(a, b):
""" Retourne le nombre d'éléments identiques de deux listes.
Par exemple, [1, 2, 3, 4, 5] et [1, 5, 3, 4, 7] ont 3 éléments communs.
"""
coincidence = 0
for i in range(len(a)):
if a[i] == b[i]:
coincidence += 1
return coincidence
def combine_clefs(s1, s2, key_len):
if len(set(s1[1]).union(set(s2[1]))) != 5:
return []
s = ['?']*key_len
for i in range(4):
s[s1[0]+i] = s1[1][i] # on recopie s1 dans s
for i in range(4):
if s[s2[0]+i] == '?' or s[s2[0]+i] == s2[1][i]: # on complète avec s2 si compatible
s[s2[0]+i] = s2[1][i]
else:
return []
return s
def eval_clef_partielle(crypto, pkey):
"""Evaluation d'une clé partielle 'pkey sur le crypto
"""
nb_alpha = len(alpha) # nombre d'alphabets c-à-d de disques disponibles
nb_car = len(alpha[0]) # nombre de caractères par alphabet
key = strkey2numkey(pkey) # conversion de la clé en liste
key_len = len(key)
debut = 0
while key[debut] == '?':
debut += 1
fin = debut
while key[fin] != '?':
fin += 1
if fin == len(key):
break
partie = [int(key[i]) for i in range(debut, fin)] # partie connue de la clé
n = len(crypto)//key_len
segments = [crypto[i*key_len:i*key_len+key_len] for i in range(n)]
nb_disques = len(partie)
best_score_p = 0
for s in segments:
idx = [0]*nb_disques
best_score_s = float('inf')
for g in range(nb_car):
for i in range(nb_disques):
idx[i] = alpha[partie[i]].find(s[i+debut])
txt = ''.join([alpha[partie[i]][(idx[i]+g)%nb_car] for i in range(nb_disques)])
score = logscore(txt)
if score < best_score_s:
best_score_s = score
best_score_p += best_score_s
return best_score_p
import time
def recherche_clef_partielle(crypto, key_len=25, nb_top=15):
start = time.time()
ancien = cherche_minimum(crypto, 0, key_len, nb_top)
for pos in range(1, key_len-3):
nouveau = cherche_minimum(crypto, pos, key_len, nb_top)
for sol0 in nouveau:
for sol1 in ancien:
if diff_listes(sol0[2], sol1[2] )>= 2: # 2 génératrices en commun au moins
key = combine_clefs(sol0, sol1, key_len)
if key:
str_key = numkey2strkey(key)
score = eval_clef_partielle(crypto, str_key)
print(str_key, ' : ', "{:10.5f}".format(score), flush=True)
ancien = nouveau
delta = int(time.time()-start)
print('----- terminé en ',delta//60, 'min', delta%60, 'sec')
def complete_clef(crypto, str_key):
nb_alpha = len(alpha) # nombre d'alphabets c-à-d de disques disponibles
nb_car = len(alpha[0]) # nombre de caractères par alphabet
key = strkey2numkey(str_key) # conversion de la clé en liste
key_len = len(key)
debut = 0
while key[debut] == '?': # recherche début de la partie connue de la clé
debut += 1
fin = debut
while key[fin] != '?': # recherche fin de la partie connue de la clé
fin += 1
if fin == len(key):
break
partie = [int(key[i]) for i in range(debut, fin)] # partie connue de la clé
if debut >= 4:
avant = True # la partie variable est avant la partie connue
nb_disques = 4+len(partie) # nombre de disques à lire
nb_pv = 4 # nombre de disques partie variable
offset = debut-4
p_avant = ['?']*(debut-4)
p_apres = ['?']*(len(key)-fin)
elif fin <= len(key) - 4:
avant = False # la partie variable est après la partie connue
nb_disques = len(partie)+4 # nombre de disques à lire
nb_pv = 4
offset = debut
p_avant = ['?']*(debut)
p_apres = ['?']*(len(key)-fin-4)
elif fin != len(key):
avant = False # la partie variable est après la partie connue
nb_disques = key_len-debut# len(key)-fin+4 # nombre de disques à lire
nb_pv = len(key)-fin
offset = debut
p_avant = ['?']*(debut)
p_apres = []
elif debut != 0:
avant = True # la partie variable est avant la partie connue
nb_disques = key_len # 4+debut # nombre de disques à lire
nb_pv = debut
offset = 0 #debut
p_avant = []
p_apres = []
else:
return str_key
n = len(crypto)//key_len
segments = [crypto[i*key_len:i*key_len+key_len] for i in range(n)]
list_idx = list(set([i for i in range(nb_alpha)])-set(partie))
perms = itertools.permutations(list_idx, nb_pv)
best_p0 = []
best_score0 = float('inf')
for p in perms:
best_score_p = 0
if avant:
pc = list(p)+partie
else:
pc = partie.copy()+list(p)
for s in segments:
idx = [0]*nb_disques
best_score_s = float('inf')
for g in range(nb_car):
for i in range(nb_disques):
idx[i] = alpha[pc[i]].find(s[i+offset])
txt = ''.join([alpha[pc[i]][(idx[i]+g)%nb_car] for i in range(nb_disques)])
score = logscore(txt)
if score < best_score_s:
best_score_s = score
best_score_p += best_score_s
if best_score_p < best_score0:
best_score0 = best_score_p
best_p0 = pc
return numkey2strkey(p_avant+best_p0+p_apres)
def clef_totale(crypto, key):
k = key
while '?' in k:
k = complete_clef(crypto, k)
print(k, flush=True)
txt = D_M94(crypto, k)
print(txt)
print(logscore(txt))
Essayons de décrypter le message donné en 1.3.
recherche_clef_partielle(crypto)
La clé partielle de plus petit logscore est la première. Complétons-là :
clef_totale(crypto, '11 6 17 7 22 ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?')
On a retrouvé le texte clair et la clé.
Sur l'une des pages du site web Cryptography & Electronic Warfare de Brooke Clarke, on trouve l'intéressante histoire des messages du Major Mauborgne.
Le Major Joseph Mauborgne a mis au point le M-94 en 1917. Pour tester sa sécurité, il a chiffré avec la même clé une série 25 messages. En avril 1918, il a envoyé à William F. Friedman et Herbert 0. Yardley les 25 premières lettres de chacun des 25 messages, pour les cryptanalyser.
Ni Friedman, ni Yardley ne sont parvenus à décrypter les messages (ils ne connaissaient pas les alphabets du M-94). En conséquence, le M-94 a été adopté par l'armée en 1921.
messages = [
'VFDJL QMMJB HSYVJ KCJTJ WDKNI',
'CGNJM ZVKQC JPRJR CGOXG UCZVC',
'CSTDT SSDJN JDKKT IXVEX VHDVK',
'OZBGF VTUEC UGTZD KYWJR VZSDG',
'QIRMB FTKBY CGAQV DQCVQ AHZGY',
'VQWRM IHDHB RQBWU LKJCS KEYUU',
'SSEIQ DWHNH QHGIK HAADN GNFBY',
'VXDVX NIGJO PCOTN GKWAX YTNWL',
'QJRLH AWTWU CYXVM BGJCR SBHWF',
'DULPK UXMVL XFUPS ULRZK PDALY',
'DCAIY LUPMB NACQE OPTLH KKRGT',
'MGODT VGUYX NHKBE WPOUR VTQOE',
'TBVEB QDXGP LCPUY AVVBK ZEOZY',
'FIJDW WBKTY GBSMB PZWYP RRZCW',
'DYVPJ CLNXE SCMF0 YPIZF PEBHM',
'MYYTJ RFMEP PHDXP ODFZO WLGLA',
'EYKKD XHTEV TRXWK CJPSG MASCY',
'LGQLV HTUIP YAUGJ PGDLH UZTKV',
'BRKTJ RGGTB HMLXX FRHOA AZVWU',
'CDUDV DBZUA ELRPO SPUJD XRZWA',
'EUFBT TWNIY HHTNW QNFVE NYGBY',
'TUTVY NGLPG TYOLI HXZQT XSGOJ',
'PBTJC CJONJ UNIXB UAQBI WNIHL',
'VHNKR XVZMD KFHUY XRNDD KXXVM',
'NNHBF VQH0B LXCYM AKFLS SSJXG'
]
On ne garde que les huit premiers messages, ce qui est suffisant pour trouver la clé (on connait les alphabets du M-94). On peut naturellement utiliser les 25 messages, mais cela allonge le temps de recherche de la clé.
crypto1 = ''
for i in range(8):
crypto1 += messages[i]
crypto1 = crypto1.replace(' ', '')
crypto1, len(crypto1)
recherche_clef_partielle(crypto1)
clef_totale(crypto1,'? ? ? ? ? ? ? 24 21 11 19 8 ? ? ? ? ? ? ? ? ? ? ? ? ?')
On a la clé !
key = '4 14 5 6 2 13 3 24 21 11 19 8 7 16 15 17 10 9 1 12 25 23 22 20 18'
Déchiffrons les 25 messages :
for m in messages:
print(D_M94(m.replace(' ', ''), key))
On retrouve, à quelques erreurs typopgraphiques près, les solutions publiées par l'ACA.
CHLORINE AND OXYGEN HAVE NOT B Chlorine and oxygen have not b
WHERE DID YOU MEET EACH OTHER I Where did you meet each other
DRINK THIS POTION QUICKLY FOR Drink this potion quickly for
WELL MAKE ME THE SAME SHAPE BUT Well, make me the same shape but
CYANOGEN IS A COLORLESS GAS IN Cyanogen is a colorless gas in
PHENOLS ARE BENZENE DERIVATI Phenols are benzene derivati
XYLONITE AND ARTIFICIAL IVOR Xylonite and artificial ivor
I WENT TO A NEW THEATRE THE PALA I went to a new theatre, the Pala
PICRIC ACID IS EXPLOSIVE AND I Picric acid is explosive and i
LLANGOLLEN IS A TOWN IN WALES A Llangollen is a town in Wales a
YVETTE ARE YOU GOING SHOPPING Yvette, are you going shopping
ORTHOPHOSPHORIC IS THE COMPO Orthophosphoric is the compo
CAOUTCHOUC IS CLOSELY ALLIED Caoutchouc is closely allied
OLEFIANT GAS ETHENE OR ETHYLE Olefiant gas, ethene or ethyle
SEE THE TERRIBLE LANK TACKLE A See the terrible tank tackle a
IT IS A THIN LIMPID LIQUID THAT It is a thin limpid liquid that
IF IT IS INSOLUBLE IN WATER IT I If it is insoluble in water it i
SILVER HAS BEEN KNOWN FROM REM Silver has been known from rem
HOT CONCENTRATED SULPHURIC A Hot concentrated sulphuric a
SMALL COEFFICIENT OF EXPANSI Small coefficient of expansi
PALLADIUM POSSESSES A POWER O Palladium possesses a power o
ABSORBING AND CONDENSING THI Absorbing and condensing thi
COMPOUNDS OF PLATINUM FORM TW Compounds of platinum form tw
GOLD OCCURS WIDELY DISTRIBUT Gold occurs widely dristribut
OXIDATIOL CAUSED BY IT PROBAB Oxidation caused by it probab
On imagine la surprise de W. Friedman quand il a trouvé, en 1941, les solutions dans les papiers du Général Mauborgne qui venait de prendre sa retraite. Certes, les messages sont en anglais, mais le vocabulaire est peu courant !
Au tout début de sa page web consacrée au M-94 Wilhelm M. Plotz donne le cryptogramme suivant :
crypto2 = '\
J U T H G F F H J T E U O N G W Z L I Z A G O P I \
I L L G Z W C Y P Q N D Z N I C S W E I L Y S U A \
L Y R M E G K B U P U Z C O S B C P I M S M R D W \
Y W O Y R I O S G Z W H F H L O U M J T D L X M H \
Y O U U C J N H S S Z S F O A Y D M A B V Z Z Q W \
O Q K C J N T T R I V O S A W V C G H X B U F P J \
V N Y P F S V H P E T K D P K P Q K L F F C M M P \
V C H N X X X T W L A K X C H R R B B W C N M H S \
Q L T K V T L Y H L O J C M Y Z R R G Q W G S V C \
K D J U Z D S E J P J Z C G C A S Q E B J W Z W Y \
B Z V B N J N U Q Y E C Z N S B I A S L Y A B V W \
G C Q U Q Y K D J X T V I M J R G F S U X Q N W G \
I H M R W J B A W X I M I N L L G E Q H Q U G B X'
crypto2 = crypto2.replace(' ', '')
crypto2, len(crypto2)
On ne garde que 8 segments pour chercher la clé :
recherche_clef_partielle(crypto2[:25*8])
Essayons de compléter la clé partielle de plus petit score :
clef_totale(crypto2,'? ? ? ? ? ? ? ? ? ? ? ? ? 16 3 9 19 24 ? ? ? ? ? ? ?')
THOSE WHO BOAST THAT THEY CAN DECIPHER A LETTER WITHOUT KNOWING ITS SUBJECT MATTER AND WITHOUT PRELIMINARY AID ARE GREATER CHARLATANS THAN THOSE WHO WOULD BOAST OF UNDERSTANDING A LANGUAGE WHICH THEY HAVE NEVER LEARNED.
DICTIONNAIRE PHILOSOPHIQUE UNDER THE ARTICLE POSTE BY VOLTAIR
FOUND IN MILITARY CRYPTANALYTICS PART ONE VOLUME ONE
CONGRATUALTIONS OBVIOUSELY YOU AREN'T A BOASTER G
Yeah! I can decipher it! I'm not a boaster :-))
Le passage est en note à la page 3 de Military Cryptanalytics Part I - volume 1 :
The application in practical, operational cryptanalysis of "probable words" or "cribs", i. e., plain text assumed or known to be present in a cryptogram, is developed in time of war into a refinement the extent and usefulness of which cannot be appreciated by the uninitiated. Even a great a thinker as Voltaire found the subject of cryptanalysis stretching his credulity to the point that he said:
"Those who boast that they can decipher a letter without knowing its subject matter, and without preliminary aid, are greater charlatans than those who would boast of understanding a language which they have never learned." - Dictionnaire Philosophique, under the article "Poste".
Sous l'article Poste du Dictionnaire Philosophique, on trouve :
On opposa à ces énigmes l’art de les déchiffrer ; mais cet art fut très-fautif et très-vain. On ne réussit qu’à faire accroire à des gens peu instruits qu’on avait déchiffré leurs lettres, et on n’eut que le plaisir de leur donner des inquiétudes. Telle est la loi des probabilités que, dans un chiffre bien fait, il y a deux cents, trois cents, quatre cents à parier contre un que dans chaque numéro vous ne devinerez pas la syllabe dont il est représentatif.
Le nombre des hasards augmente avec la combinaison de ces numéros ; et le déchiffrement devient totalement impossible quand le chiffre est fait avec un peu d’art.
Ceux qui se vantent de déchiffrer une lettre sans être instruits des affaires qu’on y traite, et sans avoir des secours préliminaires, sont de plus grands charlatans que ceux qui se vanteraient d’entendre une langue qu’ils n’ont point apprise.
Voltaire a manqué de clairvoyance sur le sujet. Il faut dire qu'à son époque les écrits sur les méthodes permettant de casser un chiffre étaient très confidentiels.
Pour les lecteurs qui voudraient vérifier / modifier / améliorer les fonctions précédentes, je propose les dépêches suivantes à décrypter. Elles sont toutes en anglais.
FYQKU RCGRF GSXNQ AFLAT ATXNZ OMLGF HTMIF FMGDG ZRQKU HSMUS
RCGGE JWVBY MEYLJ JMBPV CYQJH COZHO HLNBZ XEMUA SFLIK KSCYS
MSHQL NPXFP MMGVV SLTXW YOYFC WYNJD JQCEK BKPBU IGSKM NYDIN
QXUDU PLPQB SASSI BPATM TKZRS DFPYJ KRFJG QALQM JQWOE EOYFX
DBAMQ AMXJT EVBAD RJXRX NPHNB TA
RAEOS XVBIQ FRLHN TZJYT LHMMH FCWWD NSCOS VQFOJ FONLK EKQDL
CEMTH XMEXA JPQRL ZYBJM LGDKU LDKRF FAFLT VSHAE QVQMF RMBXZ
TMLPD ZKIAL BZULZ SXPDG JIKST ETQSL QFJSB RWIXY HEZIP MNKOS
YESBI UZPLY PNSYG IRGIZ GOVEY JFEJV AHRAF
HFMRU NCMZK BJEZT FDMSI NECUD DMHGJ LDUKK SOPTG LNIFB DBXCG
TGAFK CYXXL GAGIU UVJPW VBFYJ KTDYG DZRYH GWZCR RVAOS DJGPA
ZWQGX LFDOQ MYPHE VZMEE SITKP JGJEN GFPIV UKSJV IKZKH VFRNX
QGJML EDVAQ EPZGH OFBJP KXQBQ YMWQM WKSEV YJFTI NSSWK NFAJZ
XITK
VDCHI EAPJR GLHCZ NCXLM PGDAI ZIOXJ REXKM PVDLD KQXJF MYKOE
DPZIC YOHLL WCKMQ JGFHT YZCEZ ASFWR ZIGPP ZBIXF LGPXS HFQJX
ESKFZ NTEHY YYGQZ SCTYB GEKRM DHIQM LKUPR ZDXJS TVWDN RCCDR
RNI
Les trois cryptogrammes suivants sont chiffrés avec la même clé. Pouvez-vous les décrypter ?
ZRCVG JZGBQ CLPTE JICLO EGVJA IHHBE AVNJA ULOCC XGOCN BBIQH
MTTEY WATDQ DAODE TYCMP NB
CRHSK NDYWA EDWJE OOEQQ UQIYB RDQNI GVYQU QPEDG KPEJF NKILV
VXMKK IBZNH HPOUD FUFMV QGIKQ BZNXN ILZ
AOITF VXXDC SRTVC IKKUI DSDSK LYJSZ QHQWP NGABP XTUDU JTFMG
QKMSK VEWNR GENJ
L'attaque développée pour le cryptographe de Bazeries se transpose sans peine au M-94. Comme ce dernier a 25 rondelles au lieu de 20, la recherche des clés partielles est plus longue. On n'est pas obligé de laisser cette recherche aller jusqu'au bout : si l'on voit des clés partielles qui se recoupent, on peut les compléter dans un autre processus pour voir si on obtient le texte clair.
Pour que l'attaque réussisse, il faut un cryptogramme assez long, au moins 6 génératrices, c'est-à-dire au moins 150 caractères. En cas d'échec pour un cryptogramme court, on peut augmenter le paramètre nb_top
fixé à 15 par défaut.
Lambros Callimahos and William Friedman - Military Cryptanalytics Part I Volume 1
Aegean Park Press 1985
William Friedman - The Riverbank Publications Volume 2
Publication No. 20 Several Machine Ciphers and Methods for their Solution
Aegean Park Press 1979
Le 31/01/2017 - Contact : Rossignol@bribes.org