Open in CoCalc with one click! Kryptos

Pré-Kryptos 2017

I) César de base

In [1]:
m = "SXYVVHUTUSUIQHJHUILEYHUJHEFSBQIIYGKUIKHLYD\nWJIYNBUJJHUIJEKJYBCQHSXURYUDTKFHUCYUHSEKF"
print m
SXYVVHUTUSUIQHJHUILEYHUJHEFSBQIIYGKUIKHLYD
WJIYNBUJJHUIJEKJYBCQHSXURYUDTKFHUCYUHSEKF

Outil de base: analyse fréquentielle

In [2]:
def freqs(s):
    
    res = {}
    
    for c in s:
        if not c in res:
            res[c] = 0
        res[c] += 1

    res = sorted(res.items(), key=operator.itemgetter(1), reverse=True)
    
    img = Graphics()
    
    r = .25
    
    for i in range(len(res)):
        c,f = res[i]
        img += polygon( [(i+r, 0), (i+r, f), (i+1-r, f), (i+1-r, 0)] )
        img += text(c, (i+.5,f/2), color="white")
        
    show(img, aspect_ratio="auto")
In [3]:
freqs(m)

C'est le premier exemple: il s'agit certainement d'un chiffre de César avec E \mapsto U donc, avec clé +1610+16 \equiv -10. Pour le déchiffrer, il suffit d'appliquer la rotation inverse à l'alphabet.

In [4]:
def rot(s,i,alpha):

    res = ""
    
    for c in s:
        if c in alpha:
            res += alpha[ (alpha.index(c) + i) % len(alpha) ]
        else:
            #print u"Caractère %s ignoré" % c
            res += c
            
    return res
In [5]:
alpha = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
k = alpha.index("U") - alpha.index("E")  # 16
print rot(m,-k,alpha)
CHIFFREDECESARTRESVOIRETROPCLASSIQUESURVIN
GTSIXLETTRESTOUTILMARCHEBIENDUPREMIERCOUP

Yé !

Notez que l'espace des clés est ici suffisamment petit pour qu'on puisse toutes les essayer par force brute; le seul message qui semble avoir du sens est le bon.

In [6]:
for k in range(26):
    print k, rot(m,-k,alpha)
0 SXYVVHUTUSUIQHJHUILEYHUJHEFSBQIIYGKUIKHLYD
WJIYNBUJJHUIJEKJYBCQHSXURYUDTKFHUCYUHSEKF
1 RWXUUGTSTRTHPGIGTHKDXGTIGDERAPHHXFJTHJGKXC
VIHXMATIIGTHIDJIXABPGRWTQXTCSJEGTBXTGRDJE
2 QVWTTFSRSQSGOFHFSGJCWFSHFCDQZOGGWEISGIFJWB
UHGWLZSHHFSGHCIHWZAOFQVSPWSBRIDFSAWSFQCID
3 PUVSSERQRPRFNEGERFIBVERGEBCPYNFFVDHRFHEIVA
TGFVKYRGGERFGBHGVYZNEPUROVRAQHCERZVREPBHC
4 OTURRDQPQOQEMDFDQEHAUDQFDABOXMEEUCGQEGDHUZ
SFEUJXQFFDQEFAGFUXYMDOTQNUQZPGBDQYUQDOAGB
5 NSTQQCPOPNPDLCECPDGZTCPECZANWLDDTBFPDFCGTY
REDTIWPEECPDEZFETWXLCNSPMTPYOFACPXTPCNZFA
6 MRSPPBONOMOCKBDBOCFYSBODBYZMVKCCSAEOCEBFSX
QDCSHVODDBOCDYEDSVWKBMROLSOXNEZBOWSOBMYEZ
7 LQROOANMNLNBJACANBEXRANCAXYLUJBBRZDNBDAERW
PCBRGUNCCANBCXDCRUVJALQNKRNWMDYANVRNALXDY
8 KPQNNZMLMKMAIZBZMADWQZMBZWXKTIAAQYCMACZDQV
OBAQFTMBBZMABWCBQTUIZKPMJQMVLCXZMUQMZKWCX
9 JOPMMYLKLJLZHYAYLZCVPYLAYVWJSHZZPXBLZBYCPU
NAZPESLAAYLZAVBAPSTHYJOLIPLUKBWYLTPLYJVBW
10 INOLLXKJKIKYGXZXKYBUOXKZXUVIRGYYOWAKYAXBOT
MZYODRKZZXKYZUAZORSGXINKHOKTJAVXKSOKXIUAV
11 HMNKKWJIJHJXFWYWJXATNWJYWTUHQFXXNVZJXZWANS
LYXNCQJYYWJXYTZYNQRFWHMJGNJSIZUWJRNJWHTZU
12 GLMJJVIHIGIWEVXVIWZSMVIXVSTGPEWWMUYIWYVZMR
KXWMBPIXXVIWXSYXMPQEVGLIFMIRHYTVIQMIVGSYT
13 FKLIIUHGHFHVDUWUHVYRLUHWURSFODVVLTXHVXUYLQ
JWVLAOHWWUHVWRXWLOPDUFKHELHQGXSUHPLHUFRXS
14 EJKHHTGFGEGUCTVTGUXQKTGVTQRENCUUKSWGUWTXKP
IVUKZNGVVTGUVQWVKNOCTEJGDKGPFWRTGOKGTEQWR
15 DIJGGSFEFDFTBSUSFTWPJSFUSPQDMBTTJRVFTVSWJO
HUTJYMFUUSFTUPVUJMNBSDIFCJFOEVQSFNJFSDPVQ
16 CHIFFREDECESARTRESVOIRETROPCLASSIQUESURVIN
GTSIXLETTRESTOUTILMARCHEBIENDUPREMIERCOUP
17 BGHEEQDCDBDRZQSQDRUNHQDSQNOBKZRRHPTDRTQUHM
FSRHWKDSSQDRSNTSHKLZQBGDAHDMCTOQDLHDQBNTO
18 AFGDDPCBCACQYPRPCQTMGPCRPMNAJYQQGOSCQSPTGL
ERQGVJCRRPCQRMSRGJKYPAFCZGCLBSNPCKGCPAMSN
19 ZEFCCOBABZBPXOQOBPSLFOBQOLMZIXPPFNRBPROSFK
DQPFUIBQQOBPQLRQFIJXOZEBYFBKARMOBJFBOZLRM
20 YDEBBNAZAYAOWNPNAORKENAPNKLYHWOOEMQAOQNREJ
CPOETHAPPNAOPKQPEHIWNYDAXEAJZQLNAIEANYKQL
21 XCDAAMZYZXZNVMOMZNQJDMZOMJKXGVNNDLPZNPMQDI
BONDSGZOOMZNOJPODGHVMXCZWDZIYPKMZHDZMXJPK
22 WBCZZLYXYWYMULNLYMPICLYNLIJWFUMMCKOYMOLPCH
ANMCRFYNNLYMNIONCFGULWBYVCYHXOJLYGCYLWIOJ
23 VABYYKXWXVXLTKMKXLOHBKXMKHIVETLLBJNXLNKOBG
ZMLBQEXMMKXLMHNMBEFTKVAXUBXGWNIKXFBXKVHNI
24 UZAXXJWVWUWKSJLJWKNGAJWLJGHUDSKKAIMWKMJNAF
YLKAPDWLLJWKLGMLADESJUZWTAWFVMHJWEAWJUGMH
25 TYZWWIVUVTVJRIKIVJMFZIVKIFGTCRJJZHLVJLIMZE
XKJZOCVKKIVJKFLKZCDRITYVSZVEULGIVDZVITFLG

II) César un peu plus subtil

In [7]:
m = "PAPX WPD?YD P?D W?;D,PLWT;!PEEEDPYDRPYP,LWDTWD\nQL?!DQLT,PD!,P;DL!!PY!TZYDLDWJLW SLMP!D?!TWT;P\nDP!DLDWJZ,O,PDOLY;DWP.?PWDNPW?TKNTDP;!DPN,T!DH"
print m
PAPX WPD?YD P?D W?;D,PLWT;!PEEEDPYDRPYP,LWDTWD
QL?!DQLT,PD!,P;DL!!PY!TZYDLDWJLW SLMP!D?!TWT;P
DP!DLDWJZ,O,PDOLY;DWP.?PWDNPW?TKNTDP;!DPN,T!DH

Appliquons le même outil:

In [8]:
freqs(m)

Vue la numérotation romaine, on peut croire qu'il s'agit encore d'un chiffre de César... La question est alors sur quel alphabet: il semble y avoir de la ponctuation. Si on imagine que c'est l'alphabet A-Z en rajoutant de la ponctuation au bout, la question est: combien de signes, et dans quel ordre ? (au moins 5). Si on imagine que D vient de espace et P de E, faudrait appliquer un décalage de P-E = -11. Pour que D recule jusqu'à espace, ça nous prend donc un alphabet à (au moins) 34 caractères.

In [9]:
alpha = "ABCDEFGHIJKLMNOPQRSTUVWXYZ 1234567"
d = alpha.index('P') - alpha.index('E')  # 11
print rot(m,-d,alpha)
EXEMPLE ?N PE? PL?; ,EALI;!E111 EN GENE,AL IL 
FA?! FAI,E !,E; A!!EN!ION A L6ALPHABE! ?!ILI;E
 E! A L6O,D,E DAN; LE.?EL CEL?I7CI E;! EC,I! 4

On semble tenir quelque chose ! Il ne reste plus qu'à déterminer dans quel ordre sont écrits les signes de ponctuation à la fin de l'alphabet. Avec quelques tâtonnements on trouve aisément la position de chaque signe:

In [10]:
alpha = "ABCDEFGHIJKLMNOPQRSTUVWXYZ 1234?67"
print rot(m,-d,alpha)
EXEMPLE UN PEU PLU; ,EALI;!E111 EN GENE,AL IL 
FAU! FAI,E !,E; A!!EN!ION A L6ALPHABE! U!ILI;E
 E! A L6O,D,E DAN; LE.UEL CELUI7CI E;! EC,I! 4
In [11]:
alpha = "ABCDEFGHIJKLMNOPQRSTUVWXYZ 12;4?67"
print rot(m,-d,alpha)
EXEMPLE UN PEU PLUS ,EALIS!E111 EN GENE,AL IL 
FAU! FAI,E !,ES A!!EN!ION A L6ALPHABE! U!ILISE
 E! A L6O,D,E DANS LE.UEL CELUI7CI ES! EC,I! 4
In [12]:
alpha = "ABCDEFGHIJKLMNOPQRSTUVWXYZ 1,;4?67"
print rot(m,-d,alpha)
EXEMPLE UN PEU PLUS REALIS!E111 EN GENERAL IL 
FAU! FAIRE !RES A!!EN!ION A L6ALPHABE! U!ILISE
 E! A L6ORDRE DANS LE.UEL CELUI7CI ES! ECRI! 4
In [13]:
alpha = "ABCDEFGHIJKLMNOPQRSTUVWXYZ 1,;!?67"
print rot(m,-d,alpha)
EXEMPLE UN PEU PLUS REALISTE111 EN GENERAL IL 
FAUT FAIRE TRES ATTENTION A L6ALPHABET UTILISE
 ET A L6ORDRE DANS LE.UEL CELUI7CI EST ECRIT !
In [14]:
alpha = "ABCDEFGHIJKLMNOPQRSTUVWXYZ .,;!?67"
print rot(m,-d,alpha)
EXEMPLE UN PEU PLUS REALISTE... EN GENERAL IL 
FAUT FAIRE TRES ATTENTION A L6ALPHABET UTILISE
 ET A L6ORDRE DANS LEQUEL CELUI7CI EST ECRIT !
In [15]:
alpha = "ABCDEFGHIJKLMNOPQRSTUVWXYZ .,;!?'-"
print rot(m,-d,alpha)
EXEMPLE UN PEU PLUS REALISTE... EN GENERAL IL 
FAUT FAIRE TRES ATTENTION A L'ALPHABET UTILISE
 ET A L'ORDRE DANS LEQUEL CELUI-CI EST ECRIT !

Enseignements: si l'espace est dans notre alphabet, il est encore plus fréquent que E.

On aurait aussi pu utiliser "EEE" = "..." comme amorce...

\gimel) Atbash

Exemple d'indice donné par le contexte: le gimel (3e lettre de l'alphabet hébraïque) doit nous faire penser (via internet, ou Dan Brown) à de l'Atbash (chaque caractère remplacé par son opposé dans l'alphabet, cas particulier tout comme César de chiffre affine).

In [16]:
m = "'C!QPIR!VN!RQAVPIE"
In [17]:
def atbash(s, alpha):
    
    res = ""
    for c in s:
        res += alpha[ -alpha.index(c) ]
    return res
In [18]:
atbash(m, alpha)  # même alphabet qu'à la question précédente
Out[18]:
"C'EST RENVERSANT !"

σ\sigma) Chiffre de substitution

Cette fois-ci le σ\sigma peut nous faire penser à un chiffre de substitution général obtenu par une permutation quelconque de l'alphabet. L'analyse fréquentielle nous aide encore:

In [19]:
m = "CIFVPJIN'ZYICJVMI'PICIJCFCWOI CTMI CTICRCOXXXXI'NI'LICI\n,YT'VKIV IB'E'FIRCTXITYQYFIL,CBYL.',LMILNT'D'PJI TVZICI\n.'KKYPIQCLYMI.CEYIRVPIN.Y'TI 'TLNIE'BNVTOICJC'PLNIN.YIY\nE'FIJCFCBN'BIYZ,'TYXIKHT'PJIN.YIQCNNFYMITYQYFIL,'YLIZCP\nCJYKINVILNYCFILYBTYNI,FCPLINVIN.YIYZ,'TYSLIHFN'ZCNYIRYC\n,VPMIN.YIKYCN.ILNCTMICPICTZVTYKIL,CBYILNCN'VPIR'N.IYPVH\nJ.I,VRYTINVIKYLNTVOICPIYPN'TYI,FCPYNXI,HTLHYKIQOIN.YIYZ\n,'TYSLIL'P'LNYTICJYPNLMI,T'PBYLLIFY'CITCBYLI.VZYICQVCTK\nI.YTILNCTL.',MIBHLNVK'CPIV IN.YILNVFYPI,FCPLIN.CNIBCPIL\nCEYI.YTI,YV,FYICPKITYLNVTYI TYYKVZINVIN.YIJCFCWOXXXX"
print m
CIFVPJIN'ZYICJVMI'PICIJCFCWOI CTMI CTICRCOXXXXI'NI'LICI
,YT'VKIV IB'E'FIRCTXITYQYFIL,CBYL.',LMILNT'D'PJI TVZICI
.'KKYPIQCLYMI.CEYIRVPIN.Y'TI 'TLNIE'BNVTOICJC'PLNIN.YIY
E'FIJCFCBN'BIYZ,'TYXIKHT'PJIN.YIQCNNFYMITYQYFIL,'YLIZCP
CJYKINVILNYCFILYBTYNI,FCPLINVIN.YIYZ,'TYSLIHFN'ZCNYIRYC
,VPMIN.YIKYCN.ILNCTMICPICTZVTYKIL,CBYILNCN'VPIR'N.IYPVH
J.I,VRYTINVIKYLNTVOICPIYPN'TYI,FCPYNXI,HTLHYKIQOIN.YIYZ
,'TYSLIL'P'LNYTICJYPNLMI,T'PBYLLIFY'CITCBYLI.VZYICQVCTK
I.YTILNCTL.',MIBHLNVK'CPIV IN.YILNVFYPI,FCPLIN.CNIBCPIL
CEYI.YTI,YV,FYICPKITYLNVTYI TYYKVZINVIN.YIJCFCWOXXXX

Les quadruples X sont un peu déroutants...

In [20]:
freqs(m)

Ça peut ressembler à de l'anglais (?) Essayons

In [21]:
def subs(s,alpha,plain):
    res = ""
    for c in s:
        if c in alpha:
            res += plain[ alpha.index(c) ]
        else:
            res += c
    return res
In [28]:
alpha = "ABCDEFGHIJKLMNOPQRSTUVWXYZ ',."
plain = "ABaDEFGH JKLMNOPQRSTUVWXeZ_',."

print subs(m,alpha,plain)
a FVPJ N'Ze aJVM 'P a JaFaWO _aTM _aT aRaOXXXX 'N 'L a 
,eT'VK V_ B'E'F RaTX TeQeF L,aBeL.',LM LNT'D'PJ _TVZ a 
.'KKeP QaLeM .aEe RVP N.e'T _'TLN E'BNVTO aJa'PLN N.e e
E'F JaFaBN'B eZ,'TeX KHT'PJ N.e QaNNFeM TeQeF L,'eL ZaP
aJeK NV LNeaF LeBTeN ,FaPL NV N.e eZ,'TeSL HFN'ZaNe Rea
,VPM N.e KeaN. LNaTM aP aTZVTeK L,aBe LNaN'VP R'N. ePVH
J. ,VReT NV KeLNTVO aP ePN'Te ,FaPeNX ,HTLHeK QO N.e eZ
,'TeSL L'P'LNeT aJePNLM ,T'PBeLL Fe'a TaBeL .VZe aQVaTK
 .eT LNaTL.',M BHLNVK'aP V_ N.e LNVFeP ,FaPL N.aN BaP L
aEe .eT ,eV,Fe aPK TeLNVTe _TeeKVZ NV N.e JaFaWOXXXX

N'a pas l'air absurde... Ensuite ça prend d'autres accroches (ou fréquences doubles), en tout cas de l'observation. "N.e" revient plusieurs fois, peut-être "the" ?

In [24]:
alpha = "ABCDEFGHIJKLMNOPQRSTUVWXYZ ',."
plain = "ABaDEFGH JKLMtOPQRSTUVWXeZ*',h"
print subs(m,alpha,plain)
a FVPJ t'Ze aJVM 'P a JaFaWO *aTM *aT aRaOXXXX 't 'L a 
,eT'VK V* B'E'F RaTX TeQeF L,aBeLh',LM LtT'D'PJ *TVZ a 
h'KKeP QaLeM haEe RVP the'T *'TLt E'BtVTO aJa'PLt the e
E'F JaFaBt'B eZ,'TeX KHT'PJ the QattFeM TeQeF L,'eL ZaP
aJeK tV LteaF LeBTet ,FaPL tV the eZ,'TeSL HFt'Zate Rea
,VPM the Keath LtaTM aP aTZVTeK L,aBe Ltat'VP R'th ePVH
Jh ,VReT tV KeLtTVO aP ePt'Te ,FaPetX ,HTLHeK QO the eZ
,'TeSL L'P'LteT aJePtLM ,T'PBeLL Fe'a TaBeL hVZe aQVaTK
 heT LtaTLh',M BHLtVK'aP V* the LtVFeP ,FaPL that BaP L
aEe heT ,eV,Fe aPK TeLtVTe *TeeKVZ tV the JaFaWOXXXX

'that' est rassurant; ailleurs on dirait 'death'...

In [25]:
alpha = "ABCDEFGHIJKLMNOPQRSTUVWXYZ ',."
plain = "ABaDEFGH JdLMtOPQRSTUVWXeZ.',h"
print subs(m,alpha,plain)
a FVPJ t'Ze aJVM 'P a JaFaWO .aTM .aT aRaOXXXX 't 'L a 
,eT'Vd V. B'E'F RaTX TeQeF L,aBeLh',LM LtT'D'PJ .TVZ a 
h'ddeP QaLeM haEe RVP the'T .'TLt E'BtVTO aJa'PLt the e
E'F JaFaBt'B eZ,'TeX dHT'PJ the QattFeM TeQeF L,'eL ZaP
aJed tV LteaF LeBTet ,FaPL tV the eZ,'TeSL HFt'Zate Rea
,VPM the death LtaTM aP aTZVTed L,aBe Ltat'VP R'th ePVH
Jh ,VReT tV deLtTVO aP ePt'Te ,FaPetX ,HTLHed QO the eZ
,'TeSL L'P'LteT aJePtLM ,T'PBeLL Fe'a TaBeL hVZe aQVaTd
 heT LtaTLh',M BHLtVd'aP V. the LtVFeP ,FaPL that BaP L
aEe heT ,eV,Fe aPd TeLtVTe .TeedVZ tV the JaFaWOXXXX

'tV', sans doute 'to'

In [26]:
alpha = "ABCDEFGHIJKLMNOPQRSTUVWXYZ ',."
plain = "ABaDEFGH JdLMtOPQRSTUoWXeZ*',h"
print subs(m,alpha,plain)
a FoPJ t'Ze aJoM 'P a JaFaWO *aTM *aT aRaOXXXX 't 'L a 
,eT'od o* B'E'F RaTX TeQeF L,aBeLh',LM LtT'D'PJ *ToZ a 
h'ddeP QaLeM haEe RoP the'T *'TLt E'BtoTO aJa'PLt the e
E'F JaFaBt'B eZ,'TeX dHT'PJ the QattFeM TeQeF L,'eL ZaP
aJed to LteaF LeBTet ,FaPL to the eZ,'TeSL HFt'Zate Rea
,oPM the death LtaTM aP aTZoTed L,aBe Ltat'oP R'th ePoH
Jh ,oReT to deLtToO aP ePt'Te ,FaPetX ,HTLHed QO the eZ
,'TeSL L'P'LteT aJePtLM ,T'PBeLL Fe'a TaBeL hoZe aQoaTd
 heT LtaTLh',M BHLtod'aP o* the LtoFeP ,FaPL that BaP L
aEe heT ,eo,Fe aPd TeLtoTe *TeedoZ to the JaFaWOXXXX

"freedom" !

In [27]:
alpha = "ABCDEFGHIJKLMNOPQRSTUVWXYZ ',."
plain = "ABaDEFGH JdLMtOPQRSrUoWXemf',h"
print subs(m,alpha,plain)
a FoPJ t'me aJoM 'P a JaFaWO farM far aRaOXXXX 't 'L a 
,er'od of B'E'F RarX reQeF L,aBeLh',LM Ltr'D'PJ from a 
h'ddeP QaLeM haEe RoP the'r f'rLt E'BtorO aJa'PLt the e
E'F JaFaBt'B em,'reX dHr'PJ the QattFeM reQeF L,'eL maP
aJed to LteaF LeBret ,FaPL to the em,'reSL HFt'mate Rea
,oPM the death LtarM aP armored L,aBe Ltat'oP R'th ePoH
Jh ,oRer to deLtroO aP ePt're ,FaPetX ,HrLHed QO the em
,'reSL L'P'Lter aJePtLM ,r'PBeLL Fe'a raBeL home aQoard
 her LtarLh',M BHLtod'aP of the LtoFeP ,FaPL that BaP L
aEe her ,eo,Fe aPd reLtore freedom to the JaFaWOXXXX

"armored"... ok ça vient, en complétant on obtient:

In [28]:
alpha = "ABCDEFGHIJKLMNOPQRSTUVWXYZ ',."
plain = "*cakvl*u gds,tynbw'r*ox.emfiph"
print subs(m,alpha,plain)
a long time ago, in a galaxy far, far away.... it is a 
period of civil war. rebel spaceships, striking from a 
hidden base, have won their first victory against the e
vil galactic empire. during the battle, rebel spies man
aged to steal secret plans to the empire's ultimate wea
pon, the death star, an armored space station with enou
gh power to destroy an entire planet. pursued by the em
pire's sinister agents, princess leia races home aboard
 her starship, custodian of the stolen plans that can s
ave her people and restore freedom to the galaxy....

Enseignement: effectivement, le crawl de la Guerre des Étoiles utilise des quadruples points de suspension !

V) pour Vigenère

Cette fois on dirait que les espaces et ponctuation sont normaux, ça ressemble à un poème ou quoi. Les deux dernières lignes sont curieuses...

In [29]:
m = "elhQt'A n wegX LpB'D wxQt iyw xkzI oytxwDGA vD krKs\nIAo wkD'H jHJmqF p AGlmuVpG Gz lhzKmA.\nhlhM Hpr riwR IprCi vGt sAzAv, Hu Bup wwNGmF lvh zAt pwsvDs\nevEl d VDzq Dlh Bpv tpx zGpB Fsi fzBm szv.\nRNw, wBs, eqC Hpr'D fxXxvt l wwzxzJlC wN wmnGiq.\n\nswmEp'w d RxoA zr wGt Enwp eTI Aup AdMIA Gz fh RJzr\n'NexRt GBF oqNL ABxiwHBmF HsuCH pnGi wVD urlrlMvA.\nVy e wQtm oJ xkD qzBzo, wGtzr'D e vNCootvg Vww FtrjR,\nhwzpxlLtA nwp rE DCE ElrTvpGD euD BqFrmyDC.\n\nWBs, mw LpsrD qh VDvqpv,\nRNw, qG xenDH ur HsqCtz."
print m
elhQt'A n wegX LpB'D wxQt iyw xkzI oytxwDGA vD krKs
IAo wkD'H jHJmqF p AGlmuVpG Gz lhzKmA.
hlhM Hpr riwR IprCi vGt sAzAv, Hu Bup wwNGmF lvh zAt pwsvDs
evEl d VDzq Dlh Bpv tpx zGpB Fsi fzBm szv.
RNw, wBs, eqC Hpr'D fxXxvt l wwzxzJlC wN wmnGiq.

swmEp'w d RxoA zr wGt Enwp eTI Aup AdMIA Gz fh RJzr
'NexRt GBF oqNL ABxiwHBmF HsuCH pnGi wVD urlrlMvA.
Vy e wQtm oJ xkD qzBzo, wGtzr'D e vNCootvg Vww FtrjR,
hwzpxlLtA nwp rE DCE ElrTvpGD euD BqFrmyDC.

WBs, mw LpsrD qh VDvqpv,
RNw, qG xenDH ur HsqCtz.
In [30]:
freqs(m)

Les fréquences ne ressemblent pas à ce qu'on voyait précédemment (le "E" devrait être aperçu à une fréquence proche de celle de l'espace), ça ressemble à du polyaphabétique, peut-être plusieurs Césars entrelacés: c'est exactement le principe du chiffre de Vigenère. Pour le déchiffrer, on procède habituellement en deux temps.

1) Trouver la taille de la clé

On pourrait tout faire par analyse fréquentielle (tester différentes tailles jusqu'à retrouver le spectre fréquentiel d'une langue connue...) mais ici on peut être plus malin: quand on regarde bien on voit des mots qui se répètent.

In [31]:
mm = m.translate(None, " \n'.,")  # ignorons toute ponctuation
mm
Out[31]:
'elhQtAnwegXLpBDwxQtiywxkzIoytxwDGAvDkrKsIAowkDHjHJmqFpAGlmuVpGGzlhzKmAhlhMHprriwRIprCivGtsAzAvHuBupwwNGmFlvhzAtpwsvDsevEldVDzqDlhBpvtpxzGpBFsifzBmszvRNwwBseqCHprDfxXxvtlwwzxzJlCwNwmnGiqswmEpwdRxoAzrwGtEnwpeTIAupAdMIAGzfhRJzrNexRtGBFoqNLABxiwHBmFHsuCHpnGiwVDurlrlMvAVyewQtmoJxkDqzBzowGtzrDevNCootvgVwwFtrjRhwzpxlLtAnwprEDCEElrTvpGDeuDBqFrmyDCWBsmwLpsrDqhVDvqpvRNwqGxenDHurHsqCtz'
In [32]:
mm.index("Gz", 65) - mm.index("Gz")
Out[32]:
154
In [33]:
mm.index("RNw", 150) - mm.index("RNw")
Out[33]:
210

Deux occurences d'un même mot courant, chiffrées de la même façon, sont vraisemblablement distants d'un multiple de la longueur de la clé, soit au maximum:

In [34]:
gcd(154,210)
Out[34]:
14

Un autre mot se répète presque:

In [35]:
mm.index("WBs") - mm.index("wBs")
Out[35]:
189
In [36]:
gcd(189,14)  ## hm hm
Out[36]:
7

Vue les positions de "WBs" (en début de vers) et "wBs", la notion de lettre (indépendemment de la casse) semble respectée, ce qui serait cohérent avec un chiffrement de Vigenère sur l'alphabet "A...Za...z" -- et donc une clé de longueur 7.

2) Récupérer la clé

Ne reste plus qu'à récupérer la clé un caractère à la fois... On pourrait utiliser l'analyse fréquentielle sur chaque tranche (en prenant un caractère tous les 7) pour repérer le décalage utilisé (chose qui d'ailleurs pourrait être passablement automatisée), mais il est ici tout aussi rapide (et plus amusant) d'essayer de reconnaître des mots.

In [37]:
def vigenere(m, alpha, key):

    res = ""

    i = 0

    for c in m:
        if c in alpha:
            res += rot(c,-alpha.index(key[i]),alpha)
            i = (i+1)%len(key)
        else:
            res += c

    return res

Par exemple, on voit beaucoup: 'A, 'D (deux fois), 'H, 'w, si c'est en anglais il s'agit sans doute de 's.

In [38]:
alpha = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"
print vigenere(m, alpha, "LEAAPIA")  # je mets des A quand je ne sais pas
ThhQe's n lagX whB's sxQe ayl tkzt gyitwDrs vs grKd
AAd skD's bHyiqF a sGaiuVay Go hhzveA.
WhhM shr gewR thrre vGe kAowv, Hf tue swNreF arh zll plovDd
Wvth d Vorq shh Ban tet zGat Fhe fzme sor.
RNh, oBh, aqC shr's bxXint a swzirJay wN henveq.

sheEe's d RigA on wGe wnll eTt sue wdMts Go bh Rurr
'CaxRe yBu kqNw sBmewHmeF wouCs hnve wVo mranlMgs.
Vn a wQee oy tkD brBok, wGerr's a vNngoirg Vho FinjR,
SozetlLes nll rE ouE thrTghGs auD miFgiyDn.

OBh, iw Lakrs mh Vonqer,
RNh, iG manDs mr woqCer.

Le reste tombe rapidement:

In [39]:
alpha = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"
print vigenere(m, alpha, "LEDZPIN")
There's a lady who's sure all that glitters is gold
And she's buying a stairway to heaven.
When she gets there she knows, if the stores are all closed
With a word she can get what she came for.
Ooh, ooh, and she's buying a stairway to heaven.

There's a sign on the wall but she wants to be sure
'Cause you know sometimes words have two meanings.
In a tree by the brook, there's a songbird who sings,
Sometimes all of our thoughts are misgiven.

Ooh, it makes me wonder,
Ooh, it makes me wonder.

Enseignement: le chiffre de Vigenère, longtemps considéré suffisamment sophistiqué pour déjouer qui que ce soit, ne résiste pas longtemps aux techniques d'analyse « modernes » (disons, post-Renaissance). Comme clé, on utilise souvent une phrase à laquelle on retire les caractères en double (pour éviter de créer des coïncidences de périodicité plus courte que la longueur de la clé).

In [ ]: