Vous êtes sur la page 1sur 17

MACM 498/ CMPT 881/ MATH 800

Assignment 1 Solutions, Fall 2004

Total marks: MACM 498 : 80 CMPT 881/ MATH 800: 100


Exercise 1.7 (5 marks)
The number of keys in an Affine Cipher over Zm is m (m) > with(numtheory):
Warning, the protected name order has been redefined and unprotected

> m := 30; m := 30 > numkeys := m*phi(m); numkeys := 240 > m := 100; m := 100 > numkeys := m*phi(m); numkeys := 4000 > m := 1225; m := 1225 > numkeys := m*phi(m); numkeys := 1029000

Exercise 1.9 (5 marks)


For , 1<=a<=28, determine a mod 29 > for a to 28 do print(cat("inverse of ",a," is ",a^(-1) mod 29)) od; "inverse of 1 is 1" "inverse of 2 is 15" "inverse of 3 is 10" "inverse of 4 is 22" "inverse of 5 is 6" "inverse of 6 is 5" "inverse of 7 is 25"
( 1 )

"inverse of 8 is 11" "inverse of 9 is 13" "inverse of 10 is 3" "inverse of 11 is 8" "inverse of 12 is 17" "inverse of 13 is 9" "inverse of 14 is 27" "inverse of 15 is 2" "inverse of 16 is 20" "inverse of 17 is 12" "inverse of 18 is 21" "inverse of 19 is 26" "inverse of 20 is 16" "inverse of 21 is 18" "inverse of 22 is 4" "inverse of 23 is 24" "inverse of 24 is 23" "inverse of 25 is 7" "inverse of 26 is 19" "inverse of 27 is 14" "inverse of 28 is 28" >

Exercise 1.13 (10 marks)


Find the number of invertible 2 x 2 matrices over Zn : For this question, the brute force approach suffices. See Additional Question 3 for the general formula and proof. > f := proc(n) local a,b,c,d,count; count := 0; for a to n do for b to n do for c to n do for d to n do if igcd(a*c-b*d,n) = 1 then count:= count+1 fi od od od od; count end: Over Z6 : > f(6); 288 Over Z9 : > f(9); 3888

Over Z26 : > f(26); 157248

Exercise 1.16 (5 marks)


a) Given is the permutation 4,1,6,2,7,3,8,5 then
( 1 )

is the permutation 2,4,6,1,8,3,5,7


( 1 )

b) The following ciphertext was actually encrypted using the key so we use to decrypt it. > C:="ETEGENLMDNTNEOORDAHATECOESAHLRMI"; C := "ETEGENLMDNTNEOORDAHATECOESAHLRMI" > L:=[4,1,6,2,7,3,8,5]; L := [4, 1, 6, 2, 7, 3, 8, 5 ] > for i to 8 do pi[i]:=L[i] od: > m := 8; n := length(C); P:="": m := 8 n := 32 > for i to iquo(n,m) do for j to m do P := cat(P,C[m*(i-1)+pi[j]]) od od: > P; "GENTLEMENDONOTREADEACHOTHERSMAIL" Plaintext: Gentlemen do not read each others mail.

Exercise 1.21 (40 marks)


(a) Substitution Cipher (10 marks)
This problem may take quite a bit of trial and error to solve. Ill use uppercase letters to denote ciphertext, lowercase letters to denote plaintext > with(StringTools):
Warning, the assigned name Group now has a global binding

The Ciphertext: > C := "EMGLOSUDCGDNCUSWYSFHNSFCYKDPUMLWGYICOXYSIPJCK\ QPKUGKMGOLICGINCGACKSNISACYKZSCKXECJCKSHYSXCG\ OIDPKZCNKSHICGIWYGKKGKGOLDSILKGOIUSIGLEDSPWZU\ GFZCCNDGYYSFUSZCNXEOJNCGYEOWEUPXEZGACGNFGLKNS\ ACIGOIYCKXCJUCIUZCFZCCNDGYYSFEUEKUZCSOCFZCCNC\ IACZEJNCSHFZEJZEGMXCYHCJUMGKUCY"; C := "EMGLOSUDCGDNCUSWYSFHNSFCYKDPUMLWGYICOXYSIPJCKQPKUG \ KMGOLICGINCGACKSNISACYKZSCKXECJCKSHYSXCGOIDPKZCNKSHICGIW \ YGKKGKGOLDSILKGOIUSIGLEDSPWZUGFZCCNDGYYSFUSZCNXEOJNCGYE \

OWEUPXEZGACGNFGLKNSACIGOIYCKXCJUCIUZCFZCCNDGYYSFEUEKUZC \ SOCFZCCNCIACZEJNCSHFZEJZEGMXCYHCJUMGKUCY" > n := length(C); n := 256 > # Below, Freq will be single character frequency, Freq2 for digrams, Freq3 for trigrams > for i to n do Freq[C[i]] := 0 od: for i to n do Freq[C[i]] := Freq[C[i]] + 1 od: The most frequently occuring letters in sorted desending order: > sort(map(proc(x) if rhs(x)<9 then NULL else x fi end ,op(op(Freq))), (x,y)->rhs(x)>rhs(y)); ["C" = 37, "G" = 24, "S" = 20, "K" = 18, "I" = 15, "Y" = 15, "U" = 14, "N" = 13, "Z" = 13, "E" = 12, "O" = 10, "F" = 9 ] > for i to n-1 do Freq2[C[i..i+1]] := 0 od: for i to n-1 do Freq2[C[i..i+1]] := Freq2[C[i..i+1]] + 1 od: The most frequently occuring digrams in sorted desending order: > sort(map(proc(x) if rhs(x)<4 then NULL else x fi end ,op(op(Freq2))), (x,y)->rhs(x)>rhs(y)); ["ZC" = 7, "CG" = 7, "CK" = 5, "YS" = 5, "NC" = 5, "AC" = 5, "GO" = 5, "CN" = 5, "FZ" = 4, "CY" = 4, "GY" = 4, "SF" = 4, "GK" = 4 ] > for i to n-2 do Freq3[C[i..i+2]] := 0 od: for i to n-2 do Freq3[C[i..i+2]] := Freq3[C[i..i+2]] + 1 od: The most frequently occuring trigrams in sorted desending order: > sort(map(proc(x) if rhs(x)<2 then NULL else x fi end ,op(op(Freq3))), (x,y)->rhs(x)>rhs(y)); ["FZC" = 3, "GOI" = 3, "CCN" = 3, "YSF" = 3, "ZCC" = 3, "CGI" = 2, "GAC" = 2, "CYK" = 2, "KGO" = 2, "NCG" = 2, "ZCN" = 2, "ICG" = 2, "SAC" = 2, "GYY" = 2, "JCK" = 2, "KSH" = 2, "CND" = 2, "JNC" = 2, "DGY" = 2, "CFZ" = 2, "YYS" = 2, "NDG" = 2, "GOL" = 2, "UZC" = 2, "ZEJ" = 2, "CKS" = 2, "CKX" = 2, "CJU" = 2 ] > # from the Freq, the most frequent occuring character is C, so we suspect that # C decrypts to e, C->e # also, it is given that F->w > P := CharacterMap("FC","we",C); P := "EMGLOSUDeGDNeUSWYSwHNSweYKDPUMLWGYIeOXYSIPJeKQPKUGK \ MGOLIeGINeGAeKSNISAeYKZSeKXEeJeKSHYSXeGOIDPKZeNKSHIeGIWYGKKG \ KGOLDSILKGOIUSIGLEDSPWZUGwZeeNDGYYSwUSZeNXEOJNeGYEOWEUPX \ EZGAeGNwGLKNSAeIGOIYeKXeJUeIUZewZeeNDGYYSwEUEKUZeSOewZeeNeIA \ eZEJNeSHwZEJZEGMXeYHeJUMGKUeY" > # now C->e, F->w > # since both CN and NC are frequent, and ZCN, FZC are

> # since both CN and NC are frequent, and ZCN, FZC are frequent. # we know WHE is likely, so Z->h, and N->l, therefore ZCN->hel, FZC->whe > P := CharacterMap("FCZN","wehl",C); P := "EMGLOSUDeGDleUSWYSwHlSweYKDPUMLWGYIeOXYSIPJeKQPKUGKM \ GOLIeGIleGAeKSlISAeYKhSeKXEeJeKSHYSXeGOIDPKhelKSHIeGIWYGKKGKGO \ LDSILKGOIUSIGLEDSPWhUGwheelDGYYSwUShelXEOJleGYEOWEUPXEhGAeGl \ wGLKlSAeIGOIYeKXeJUeIUhewheelDGYYSwEUEKUheSOewheeleIAehEJleSHwhEJ \ hEGMXeYHeJUMGKUeY" > # now C->e, F->w, Z->h, N->l # only frequent trigram ending with ZC is UZC, and U is quite frequent, so # likely it is U->t, UZC->the > P := CharacterMap("FCZNU","wehlt",C); P := "EMGLOStDeGDletSWYSwHlSweYKDPtMLWGYIeOXYSIPJeKQPKtGKMGOLI \ eGIleGAeKSlISAeYKhSeKXEeJeKSHYSXeGOIDPKhelKSHIeGIWYGKKGKGOLDSI \ LKGOItSIGLEDSPWhtGwheelDGYYSwtShelXEOJleGYEOWEtPXEhGAeGlwGLKlS \ AeIGOIYeKXeJteIthewheelDGYYSwEtEKtheSOewheeleIAehEJleSHwhEJhEGMXeYH \ eJtMGKteY" > # G, Y, S are all quite frequent, and appears as GYYSw # guess that GYYSw->arrow # G->a, Y->r, S->o > P := CharacterMap("FCZNUGYS","wehltaro",C); P := "EMaLOotDeaDletoWrowHlowerKDPtMLWarIeOXroIPJeKQPKtaKMaOLIeaIleaA \ eKolIoAerKhoeKXEeJeKoHroXeaOIDPKhelKoHIeaIWraKKaKaOLDoILKaOItoIaLED \ oPWhtawheelDarrowtohelXEOJlearEOWEtPXEhaAealwaLKloAeIaOIreKXeJteIthewhee \ lDarrowEtEKtheoOewheeleIAehEJleoHwhEJhEaMXerHeJtMaKter" > > # we see wheelDarrow, which should be wheelbarrow, so D->b > P := CharacterMap("FCZNUGYSD","wehltarob",C); P := "EMaLOotbeabletoWrowHlowerKbPtMLWarIeOXroIPJeKQPKtaKMaOLIeaIleaAe \ KolIoAerKhoeKXEeJeKoHroXeaOIbPKhelKoHIeaIWraKKaKaOLboILKaOItoIaLEboP \ WhtawheelbarrowtohelXEOJlearEOWEtPXEhaAealwaLKloAeIaOIreKXeJteIthewheelba \ rrowEtEKtheoOewheeleIAehEJleoHwhEJhEaMXerHeJtMaKter" > # K is quite frequent, and GK, CK are frequent, so A-, E- are frequent # also I is quite frequent, but digrams are not frequent. # wheeleI is probably wheeled, so I->d > # guess that K->s, since Khoe is probably shoe > P := CharacterMap("FCZNUGYSDKI","wehltarobsd",C); P := "EMaLOotbeabletoWrowHlowersbPtMLWardeOXrodPJesQPstasMaOLdeadleaAes \ oldoAershoesXEeJesoHroXeaOdbPshelsoHdeadWrassasaOLbodLsaOdtodaLEboPWhta \

wheelbarrowtohelXEOJlearEOWEtPXEhaAealwaLsloAedaOdresXeJtedthewheelbarrow \ EtEstheoOewheeledAehEJleoHwhEJhEaMXerHeJtMaster" > # EtEs should probably be itis, so guess E->i # first word is iM, so guess M->m # oOe is probably one, so O->n > P := CharacterMap("FCZNUGYSDKIEMO","wehltarobsdimn",C); P := "imaLnotbeabletoWrowHlowersbPtmLWardenXrodPJesQPstasmanLdeadleaAesold \ oAershoesXieJesoHroXeandbPshelsoHdeadWrassasanLbodLsandtodaLiboPWhtawheel \ barrowtohelXinJlearinWitPXihaAealwaLsloAedandresXeJtedthewheelbarrowitistheonew \ heeledAehiJleoHwhiJhiamXerHeJtmaster" > # now it is easy to finish it off: # bPt is probably but, so P->u # Wrow is probably grow, so W->g # maL is probably may, so L->y > P := CharacterMap("FCZNUGYSDKIEMOPWL","wehltarobsdimnugy",C); P := "imaynotbeabletogrowHlowersbutmygardenXroduJesQustasmanydeadleaAesoldo \ AershoesXieJesoHroXeandbushelsoHdeadgrassasanybodysandtodayiboughtawheelbarro \ wtohelXinJlearingituXihaAealwaysloAedandresXeJtedthewheelbarrowitistheonewheeled \ AehiJleoHwhiJhiamXerHeJtmaster" > # Hlowers is probably flowers, so H->f # XroduJes is probably produces, so X->p, J->c # Qust is probably just, so Q->j # leaAes is probably leaves, so A->v > P := CharacterMap("FCZNUGYSDKIEMOPWLHXJQA","wehltarobsdimnugyfpcjv ",C); P := "imaynotbeabletogrowflowersbutmygardenproducesjustasmanydeadleavesoldovers \ hoespiecesofropeandbushelsofdeadgrassasanybodysandtodayiboughtawheelbarrowtohel \ pinclearingitupihavealwayslovedandrespectedthewheelbarrowitistheonewheeledvehicleo \ fwhichiamperfectmaster" The plaintext is as follows: "I may not be able to grow flowers, but my garden produces just as many dead leaves, old overshoes, pieces of rope, and bushels of dead grass as anybodys, and today I bought a wheelbarrow to help in clearing it up. I have always loved and respected the wheelbarrow. It is the one wheeled vehicle of which I am perfect master"; >

(b) Vigenere Cipher (10 marks)


> restart; > Sigma := "ABCDEFGHIJKLMNOPQRSTUVWXYZ": for i from 0 to 25 do temp := Sigma[i+1]; char[i] := temp; code[temp] := i;

code[temp] := i; od: The cipher text y: > Y:="KCCPKBGUFDPHQTYAVINRRTMVGRKDNBVFDETDGILTXRGUD\ DKOTFMBPVGEGLTGCKQRACQCWDNAWCRXIZAKFTLEWRPTYC\ QKYVXCHKFTPONCQQRHJVAJUWETMCMSPKQDYHJVDAHCTRL\ SVSKCGCZQQDZXGSFRLSWCWSJTBHAFSIASPRJAHKJRJUMV\ GKMITZHFPDISPZLVLGWTFPLKKEBDPGCEBSHCTJRWXBAFS\ PEZQNRWXCVYCGAONWDDKACKAWBBIKFTIOVKCGGHJVLNHI\ FFSQESVYCLACNVRWBBIREPBBVFEXOSCDYGZWPFDTKFQIY\ CWHJVLNHIQIBTKHJVNPIST": Apply the Wolfe Friedman test, and compute the indices of coincidence to find m: > n := length(Y): L := 8: A := Matrix(L,L): for m to L do q := iquo(n,m,r); for i to m do y := cat(seq(Y[m*j+i],j=0..q)); freq := Array(0..25); N := length(y); for j to N do freq[code[y[j]]] := freq[code[y[j]]] + 1 od; A[m,i] := evalf(add(freq[j]*(freq[j]-1)/N/(N-1),j=0..25),3) od od: A; 0.0409 0 0 0 0 0 0 0 0.0385 0.0471 0 0 0 0 0 0 0.0559 0.0481 0.0483 0 0 0 0 0 0.0373 0.0427 0.0376 0.0491 0 0 0 0 0.0426 0.0430 0.0326 0.0353 0.0430 0 0 0 0.0627 0.0838 0.0494 0.0649 0.0429 0.0734 0 0 0.0306 0.0443 0.0434 0.0408 0.0443 0.0443 0.0408 0 0.0332 0.0407 0.0337 0.0407 0.0395 0.0453 0.0407 0.0546 It appears that m=6 has values closest to 0.065 Now, we try to find the key by calculating Mg values > m := 6; m := 6 Compute y1,y2,y3,...,y6 > q := iquo(n,m,r):

> q := iquo(n,m,r): for i to m do y[i] := cat(seq(Y[m*j+i],j=0..q)); od; y1 := "KGQNGVGGTGCQWAWQHNJEPJTKQFWAPJGHPWKCTAQVNCIVJFVNIV \ CPQJQJT" y2 := "CUTRRFIUFEKCCKRKKCVTKVRCDRSFRRKFZTEEJFNYWKKKVFYVRF \ DFIVIV" y3 := "CFYRKDLDMGQWRFPYFQAMQDLGZLJSJJMPLFBBRSRCDAFCLSCREE \ YDYLBN" y4 := "PDATDETDBLRDXTTVTQJCDASCXSTIAUIDVPDSWPWGDWTGNQLWPX \ GTCNTP" y5 := "KPVMNTXKPTANILYXPRUMYHVZGWBAHMTILLPHXEXAKBIGHEABB \ OZKWHKI" y6 := "BHIVBDROVGCAZECCOHWSHCSQSCHSKVZSGKGCBZCOABOHISCBB \ SWFHIHS" > p := [.082, .015, .028, .043, .127, .022, .020, .061, .070, .002, .008, .040, .024, .067, .075, .019, .001, .060, .063, .091, .028, .010, .023, .001, .020, .001]: > Mg := proc(x::string, g::integer) local i,f,n; n := length(x); f := Array(0..25); for i to n do f[code[x[i]]] := f[code[x[i]]] + 1 od; evalf(add(p[1+i]*f[i+g mod 26],i=0..25)/n,5); end: > for i to m do seq(j=evalf(Mg(y[i],j),3),j=0..25) od; 0 = 0.0316, 1 = 0.0358, 2 = 0.0646, 3 = 0.0389, 4 = 0.0338, 5 = 0.0420, 6 = 0.0367, 7 = 0.0311, 8 = 0.0422, 9 = 0.0460, 10 = 0.0252, 11 = 0.0338, 12 = 0.0383, 13 = 0.0422, 14 = 0.0381, 15 = 0.0463, 16 = 0.0361, 17 = 0.0401, 18 = 0.0422, 19 = 0.0334, 20 = 0.0303, 21 = 0.0393, 22 = 0.0434, 23 = 0.0344, 24 = 0.0415, 25 = 0.0335 0 = 0.0380, 1 = 0.0395, 2 = 0.0489, 3 = 0.0417, 4 = 0.0395, 5 = 0.0362, 6 = 0.0454, 7 = 0.0304, 8 = 0.0268, 9 = 0.0355, 10 = 0.0449, 11 = 0.0306, 12 = 0.0350, 13 = 0.0477, 14 = 0.0405, 15 = 0.0330, 16 = 0.0355, 17 = 0.0706, 18 = 0.0365, 19 = 0.0299, 20 = 0.0291, 21 = 0.0356, 22 = 0.0296, 23 = 0.0379, 24 = 0.0458, 25 = 0.0369 0 = 0.0351, 1 = 0.0365, 2 = 0.0335, 3 = 0.0379, 4 = 0.0355, 5 = 0.0414, 6 = 0.0276, 7 = 0.0380, 8 = 0.0338, 9 = 0.0421, 10 = 0.0412, 11 = 0.0455, 12 = 0.0401, 13 = 0.0429,

14 = 0.0365, 15 = 0.0316, 16 = 0.0349, 17 = 0.0391, 18 = 0.0424, 19 = 0.0314, 20 = 0.0391, 21 = 0.0327, 22 = 0.0348, 23 = 0.0435, 24 = 0.0587, 25 = 0.0451 0 = 0.0454, 1 = 0.0381, 2 = 0.0438, 3 = 0.0371, 4 = 0.0368, 5 = 0.0379, 6 = 0.0311, 7 = 0.0333, 8 = 0.0390, 9 = 0.0375, 10 = 0.0368, 11 = 0.0512, 12 = 0.0407, 13 = 0.0313, 14 = 0.0349, 15 = 0.0660, 16 = 0.0371, 17 = 0.0297, 18 = 0.0390, 19 = 0.0408, 20 = 0.0253, 21 = 0.0351, 22 = 0.0413, 23 = 0.0332, 24 = 0.0347, 25 = 0.0440 0 = 0.0403, 1 = 0.0333, 2 = 0.0344, 3 = 0.0396, 4 = 0.0449, 5 = 0.0336, 6 = 0.0430, 7 = 0.0460, 8 = 0.0468, 9 = 0.0342, 10 = 0.0345, 11 = 0.0359, 12 = 0.0342, 13 = 0.0350, 14 = 0.0335, 15 = 0.0442, 16 = 0.0340, 17 = 0.0358, 18 = 0.0351, 19 = 0.0558, 20 = 0.0409, 21 = 0.0353, 22 = 0.0434, 23 = 0.0445, 24 = 0.0306, 25 = 0.0320 0 = 0.0416, 1 = 0.0382, 2 = 0.0367, 3 = 0.0421, 4 = 0.0388, 5 = 0.0268, 6 = 0.0333, 7 = 0.0394, 8 = 0.0365, 9 = 0.0341, 10 = 0.0477, 11 = 0.0345, 12 = 0.0249, 13 = 0.0367, 14 = 0.0704, 15 = 0.0422, 16 = 0.0320, 17 = 0.0322, 18 = 0.0387, 19 = 0.0327, 20 = 0.0401, 21 = 0.0414, 22 = 0.0353, 23 = 0.0371, 24 = 0.0393, 25 = 0.0485 We can find the key from looking at how many we should shift to get a value that is closest to 0.0657 we guess that y1 was obtained from shifting two places, so K1 = 2, similarly, K2 = 17, K3 = 24, K4 = 15, K5 = 19, K6 = 14 > keyword := cat(char[2],char[17],char[24],char[15],char[19],char[14]); keyword := "CRYPTO" So the keyword is CRYPTO Now, we can decrypt the message: > decrypt := proc(Y,K) local n,m,y,k,i,x,c; n,m := length(Y),length(K); y := [seq(code[Y[i]],i=1..n)]; k := [seq(code[K[i]],i=1..m)]; c := 1; for i to n do x[i] := y[i]-k[c] mod 26; c := c+1; if c=7 then c:=1 fi; od; cat(seq(char[x[i]],i=1..n)); end: > decrypt(Y,keyword); "ILEARNEDHOWTOCALCULATETHEAMOUNTOFPAPERNEEDEDFORAROOM \ WHENIWASATSCHOOLYOUMULTIPLYTHESQUAREFOOTAGEOFTHEWALLS \ BYTHECUBICCONTENTSOFTHEFLOORANDCEILINGCOMBINEDANDDOUBLE \ ITYOUTHENALLOWHALFTHETOTALFOROPENINGSSUCHASWINDOWSANDD \

OORSTHENYOUALLOWTHEOTHERHALFFORMATCHINGTHEPATTERNTHEN \ YOUDOUBLETHEWHOLETHINGAGAINTOGIVEAMARGINOFERRORANDTHEN \ YOUORDERTHEPAPER" I learned how to calculate the amount of paper needed for a room when I was at school. You multiply the square footage of the walls by the cubic contents of the floor and ceiling combined, and double it. You then allow half the total for openings such as windows and doors. Then you allow the other half for matching the pattern. Then you double the whole thing again to give a margin of error, and then you order the paper.

(c) Affine Cipher (10 marks)


The cipher text: > Y := "KQEREJEBCPPCJCRKIEACUZBKRVPKRBCIBQCARBJCVFCUP\ KRIOFKPACUZQEPBKRXPEIIEABDKPBCPFCDCCAFIEABDKP\ BCPFEQPKAZBKRHAIBKAPCCIBURCCDKDCCJCIDFUIXPAFF\ ERBICZDFKABICBBENEFCUPJCVKABPCYDCCDPKBCOCPERK\ IVKSCPICBRKIJPKABI": The plaintext here is actually in French, but we can still analyze the frequencies > n := length(Y): freq := Array(0..25): for i to n do freq[code[Y[i]]] := freq[code[Y[i]]]+1: od: > seq(char[i]=freq[i],i=0..25); "A" = 13, "B" = 21, "C" = 32, "D" = 9, "E" = 13, "F" = 10, "G" = 0, "H" = 1, "I" = 16, "J" = 6, "K" = 20, "L" = 0, "M" = 0, "N" = 1, "O" = 2, "P" = 20, "Q" = 4, "R" = 12, "S" = 1, "T" = 0, "U" = 6, "V" = 4, "W" = 0, "X" = 2, "Y" = 1, "Z" = 4 The most frequently occuring letter is C, and the second most frequent is B. We can guess that C decrypts to E and B decrypts to T: Let (a x + b ) mod 26 be the encryption function, then we can solve for a and b based on the guess > msolve({a*code["E"]+b = code["C"], a*code["T"]+b = code["B"]}, 26); {a = 19, b = 4 } Therefore, y = (19 x + 4 ) mod 26, > (y-4)*19^(-1) mod 26; 11 y + 8 x = (11 y + 8 ) mod 26 > d := y->(y-4)*19^(-1) mod 26; > cat(seq(char[d(code[Y[i]])],i=1..n)); "OCANADATERREDENOSAIEUXTONFRONTESTCEINTDEFLEURONSGLORIEU \ XCARTONBRASSAITPORTERLEPEEILSAITPORTERLACROIXTONHISTOIREES \ TUNEEPOPEEDESPLUSBRILLANTSEXPLOITSETTAVALEURDEFOITREMPEEP \ ROTEGERANOSFOYERSETNOSDROITS" The plaintext is actualy the French lyrics to "O Canada":

O Canada! Terre de nos aieux. Ton Front est ceint, de fleurons glorieux. Car ton bras sait porter lepee, il sait porter lacroix. Ton histoire est un epopee, des plus brillants exploits. Et ta valeur, de foi trempee, protegera nos foyers et nos droits.

(d) unspecified cipher (10 marks)


The ciphertext: > Y := "BNVSNSIHQCEELSSKKYERIFJKXUMBGYKAMQLJTYAVFBKVT\ DVBPVVRJYYLAOKYMPQSCGDLFSRLLPROYGESEBUUALRWXM\ MASAZLGLEDFJBZAVVPXWICGJXASCBYEHOSNMULKCEAHTQ\ OKMFLEBKFXLRRFDTZXCIWBJSICBGAWDVYDHAVFJXZIBKC\ GJIWEAHTTOEWTUHKRQVVRGZBXYIREMMASCSPBNLHJMBLR\ FFJELHWEYLWISTFVVYFJCMHYUYRUFSFMGESIGRLWALSWM\ NUHSIMYYITCCQPZSICEHBCCMZFEGVJYOCDEMMPGHVAAUM\ ELCMOEHVLTIPSUYILVGFLMVWDVYDBTHFRAYISYSGKVSUU\ HYHGGCKTMBLRX": It is actually encrypted using Vigenere Cipher. Apply the Wolfe Friedman test, and compute the indices of coincidence to find m: > n := length(Y): L := 8: A := Matrix(L,L): for m to L do q := iquo(n,m,r); for i to m do y := cat(seq(Y[m*j+i],j=0..q)); freq := Array(0..25); N := length(y); for j to N do freq[code[y[j]]] := freq[code[y[j]]] + 1 od; A[m,i] := evalf(add(freq[j]*(freq[j]-1)/N/(N-1),j=0..25),3) od od: A; 0.0414 0 0 0 0 0 0 0 0.0443 0.0462 0 0 0 0 0 0 0.0443 0.0479 0.0484 0 0 0 0 0 0.0435 0.0563 0.0465 0.0479 0 0 0 0 0.0450 0.0425 0.0396 0.0433 0.0370 0 0 0 0.0512 0.0613 0.0550 0.0709 0.0555 0.0698 0 0 0.0398 0.0447 0.0443 0.0377 0.0464 0.0334 0.0464 0 0.0583 0.0555 0.0509 0.0463 0.0379 0.0647 0.0464 0.0502 It appears that m=6 has values closest to 0.065

Now, we try to find the key by calculating Mg values > m := 6; m := 6 Compute y1,y2,y3,...,y6 > q := iquo(n,m,r): for i to m do y[i] := cat(seq(Y[m*j+i],j=0..q)); od; y1 := "BILEXKTKPYMDLEAMGBXXEUHFXTBGDXGHTVXMBBELVMUEWNY \ QEZYMAMTIMDAGHKX" y2 := "NHSRUAYVVLPLPSLALZWAHLTLLZJAHZJTUVYANLLWVHFSAUYPHF \ OPUOILVBYKYT" y3 := "VQSIMMATVAQFRERSEAISOKQERXSWAIITHRISLRHIYYSILHIZBECGM \ EPVWTIVHM" y4 := "SCKFBQVDROSSOBWADVCCSCOBRCIDVBWOKGRCHFWSFUFGSSTSC \ GDHEHSGDHSSGB" y5 := "NEKJGLFVJKCRYUXZFVGBNEKKFICVFKEERZESJFETJYMRWICICVEVL \ VUFVFYUGL" y6 := "SEYKYJBBYYGLGUMLJPJYMAMFDWBYJCAWQBMPMJYFCRGLMMCC \ MJMACLYLYRSUCR" > # Mg was defined above in part b) for i to m do seq(j=Mg(y[i],j),j=0..25) od; 0 = 0.040746, 1 = 0.035936, 2 = 0.033413, 3 = 0.034286, 4 = 0.045556, 5 = 0.037079, 6 = 0.039714, 7 = 0.034809, 8 = 0.046476, 9 = 0.034540, 10 = 0.036540, 11 = 0.035730, 12 = 0.044349, 13 = 0.035508, 14 = 0.028016, 15 = 0.040809, 16 = 0.042730, 17 = 0.033841, 18 = 0.034968, 19 = 0.060984, 20 = 0.041016, 21 = 0.032635, 22 = 0.033714, 23 = 0.045524, 24 = 0.034349, 25 = 0.037730 0 = 0.040822, 1 = 0.034935, 2 = 0.032710, 3 = 0.043919, 4 = 0.037323, 5 = 0.036645, 6 = 0.036839, 7 = 0.068064, 8 = 0.036871, 9 = 0.032564, 10 = 0.028419, 11 = 0.046758, 12 = 0.030742, 13 = 0.039177, 14 = 0.036581, 15 = 0.035806, 16 = 0.033661, 17 = 0.039306, 18 = 0.047919, 19 = 0.039726, 20 = 0.045355, 21 = 0.033548, 22 = 0.042274, 23 = 0.036064, 24 = 0.035516, 25 = 0.029452 0 = 0.052935, 1 = 0.035516, 2 = 0.030984, 3 = 0.037871, 4 = 0.061097, 5 = 0.036210, 6 = 0.030435, 7 = 0.036887, 8 = 0.045790, 9 = 0.028306, 10 = 0.033742, 11 = 0.035693, 12 = 0.038629, 13 = 0.039645, 14 = 0.044403, 15 = 0.044016, 16 = 0.038839, 17 = 0.041742, 18 = 0.034984, 19 = 0.038790, 20 = 0.037113, 21 = 0.034129,

22 = 0.034097, 23 = 0.036613, 24 = 0.035129, 25 = 0.037403 0 = 0.042016, 1 = 0.043226, 2 = 0.039935, 3 = 0.043484, 4 = 0.036532, 5 = 0.031758, 6 = 0.030548, 7 = 0.032484, 8 = 0.028500, 9 = 0.033419, 10 = 0.052435, 11 = 0.036258, 12 = 0.029564, 13 = 0.040226, 14 = 0.069193, 15 = 0.042871, 16 = 0.032855, 17 = 0.033613, 18 = 0.044774, 19 = 0.028984, 20 = 0.032710, 21 = 0.033871, 22 = 0.031726, 23 = 0.033887, 24 = 0.042968, 25 = 0.053161 0 = 0.037742, 1 = 0.042806, 2 = 0.048516, 3 = 0.035387, 4 = 0.042677, 5 = 0.038564, 6 = 0.041855, 7 = 0.035048, 8 = 0.032081, 9 = 0.034790, 10 = 0.035371, 11 = 0.035113, 12 = 0.033839, 13 = 0.046952, 14 = 0.035290, 15 = 0.028548, 16 = 0.040177, 17 = 0.063193, 18 = 0.041790, 19 = 0.033613, 20 = 0.037919, 21 = 0.037613, 22 = 0.030823, 23 = 0.038371, 24 = 0.042419, 25 = 0.030500 0 = 0.029210, 1 = 0.034468, 2 = 0.036145, 3 = 0.027258, 4 = 0.037290, 5 = 0.048871, 6 = 0.033952, 7 = 0.040548, 8 = 0.043758, 9 = 0.045145, 10 = 0.038226, 11 = 0.039871, 12 = 0.034774, 13 = 0.037629, 14 = 0.033548, 15 = 0.027887, 16 = 0.039532, 17 = 0.036629, 18 = 0.040290, 19 = 0.038629, 20 = 0.053500, 21 = 0.036935, 22 = 0.033403, 23 = 0.035693, 24 = 0.064548, 25 = 0.033258 From the above result closest to 0.0657, we see that K1 = 19, K2 = 7, K3 = 4, K4 = 14, K5 = 17, K6 = 24 > keyword := cat(char[19],char[7],char[4],char[14],char[17],char[24]); keyword := "THEORY" So the keyword is THEORY Now, we can decrypt the message: > # decrypt was defined in part b) decrypt(Y,keyword); "IGREWUPAMONGSLOWTALKERSMENINPARTICULARWHODROPPEDWORD \ SAFEWATATIMELIKEBEANSINAHILLANDWHENIGOTTOMINNEAPOLISWHER \ EPEOPLETOOKALAKEWOBEGONCOMMATOMEANTHEENDOFASTORYICOU \ LDNTSPEAKAWHOLESENTENCEINCOMPANYANDWASCONSIDEREDNOTTO \ OBRIGHTSOIENROLLEDINASPEECHCOURSETAUGHTBYORVILLESANDTHEF \ OUNDEROFREFLEXIVERELAXOLOGYASELFHYPNOTICTECHNIQUETHATEN \ ABLEDAPERSONTOSPEAKUPTOTHREEHUNDREDWORDSPERMINUTE" The plaintext is as follows: I grew up among slow talkers, men in particular, who dropped words a few at a time like beans in a hill, and when I got to Minneapolis where people took a Lake Wobegon comma to mean the end of a story, I couldnt speak a whole sentence in company and was considered not too bright. So I enrolled in a speech course taught by Orville Sand, the founder of reflexive relaxology, a self-hypnotic technique that enabled a person to speak up to three hundred words per minute.

Additional question 1 (5 marks)


We know a plaintext, ciphertext pair for the Hill Cipher, and blocksize m is 2 The ciphertext: > Y := "GCGJFA"; Y := "GCGJFA" The plaintext: > X := "SELLIT"; X := "SELLIT" > m := 2; m := 2 > for i to 3 do x[i] := <code[X[m*i-1]],code[X[m*i]]>; y[i] := <code[Y[m*i-1]],code[Y[m*i]]>; od: It turns out that we can solve using the blocks 2 and 3 > A := Matrix(<x[2]|x[3]>); 11 8 A := 11 19 > B := Matrix(<y[2]|y[3]>); 6 5 B := 9 0 We have B = KA Now, find the inverse of A > AI := Inverse(A) mod 26; 21 24 AI := 7 19 Then the key K would be uniquely determined since the inverse of A is unique i.e. K = B . (A ) > K := map(modp,B.AI,26); 5 5 K := 7 8 So, Oscar has enough information to determine the key uniquely
( 1 )

Additional question 2 (10 marks)


The question should have been to reproduce row 2 of table 1.4, not table 1.14 Ciphertext: > Y := "CHREEVOAHMAERATBIAXXWTNXBEEOPHBSBQMQEQERBW\ RVXUOAKXAOSXXWEAHBWGJMMQMNKGRFVGXWTRZXWIAK\ LXFPSKAUTEMNDCMGTSXMXBTUIADNGMGPSRELXNJELX\ VRVPRTULHDNQWTWDTYGBPHXTFALJHASVBFXNGLLCHR\

VRVPRTULHDNQWTWDTYGBPHXTFALJHASVBFXNGLLCHR\ ZBWELEKMSJIKNBHWRJGNMGJSGLXFEYPHAGNRBIEQJT\ AMRVLCRREMNDGLXRRIMGNSNRWCHRQHAEYEVTAQEBBI\ PEEWEVKAKOEWADREMXMTBHHCHRTKDNVRZCHRCLQOHP\ WQAIIWXNRMGWOIIFKEE": > n := length(Y); n := 313 > m := 5; m := 5 Compute y2, i.e. row 2 > q := iquo(n,m,r): y[2] := cat(seq(Y[m*j+2],j=0..q)); y2 := "HOEITESEWOOEGMFTIFUDSTNSNVTNDPASNHESBGSEGEMRDRSHEAIEOR \ THNHOANOE" > # Mg was defined in question 1.21 part b) seq(evalf(Mg(y[2],j),2),j=0..25); 0.069, 0.044, 0.032, 0.035, 0.044, 0.034, 0.036, 0.033, 0.029, 0.031, 0.042, 0.045, 0.040, 0.045, 0.046, 0.042, 0.037, 0.032, 0.034, 0.037, 0.032, 0.034, 0.043, 0.032, 0.026, 0.047 >

Additional question 3 (20 marks) (CMPT 881 and MATH 800)


Here, let N(n ) denote the number of invertible 2 x 2 matrices over Zn let p be a prime. First, we show that N(p ) = (p 2 1 ) (p 2 p ). (5 marks) We can use the hint from the book that Zp is a field, and a matrix over a field is invertible iff its rows are linearly independent. We have p 2 1 choices for the first row (any row vectors over Zp other than the zero vector) We have p 2 p choices for the second row (any row vectors over Zp that are not scalar multiple of the first row) Therefore, N(p ) = (p 2 1 ) (p 2 p )

Next, we want to show that N(p 2 ) = p 4 (p 2 1 ) (p 2 p ), (10 marks) or more generally, N(p k ) = p (p 2 1 ) (p 2 p ) Let Mn denote the set of 2 x 2 matrices over Zn For each 2 k, define the function fk as follows: fk : M (k 1 ) x Mp -> M k
p p (4 (k 1 ))

fk(A, B ) = p A + B, where A is a 2 x 2 matrix over Zp , B is a 2 x 2 matrix over Zp First, we show that fk is a bijection. Suppose fk(A, B ) = fk(AA, BB), then p A + B = p AA + BB we can rewrite as p (A AA) = BB B.

(k 1 )

we can rewrite as p (A AA) = BB B. In other words, p divides each element of BB B, but BB B is a matrix over Zp, so BB B = 0, i.e. BB = B Now, p (A AA) = 0, so p divides each element of A AA, but A AA is a matrix over Zp , so A = AA Therefore, fk is injective. Also, the domain and codomain of fk have the same cardinality, so fk is a bijection. Second, we show that fk(A, B ) is invertible over Zp k iff B is invertible over Zp. Recall that a matrix C over Zp k is invertible iff det(C) is invertible over Zp k, i.e. gcd(det(C), p k) = 1 In other words, C is invertible over Zp k iff det(C) mod p <> 0 We can simply compute the determinant mod p: det(fk(A, B )) = det(p A + B) = det(B) (mod p) It is now easy to show that fk(A, B ) is invertible over Zp k iff B is invertible over Zp. From the bijection, we know that every matrix C over Zp k can be uniquely decomposed into matrices A, B such that A is a 2 x 2 matrix over Zp , B is a 2 x 2 matrix over Zp and C = p A + B. Furthermore, C is invertible over Zp k iff B is invertible over Zp. Now, we can count the number of invertible 2 x 2 matrices C over Zp k: We already know that the number of invertible matrices B over Zp is (p 2 1 ) (p 2 p ) A can be any matrix over Zp Therefore, N(p k ) = p
(4 (k 1 )) (k 1 ) (k 1 ) (k 1 ) (k 1 )

, so there are p

(4 (k 1 ))

of them.

(p 2 1 ) (p 2 p )

Finally, we want to show that N(p q ) = N(p ) N(q ) for primes p,q (5 marks) More generally, N(a b ) = N(a ) N(b ) for any a,b where a,b are relatively prime This time, we can also define a function g where g : Ma x Mb --> Ma b g(A, B ) = b A + a B, where A is a 2 x 2 matrix over Za, B is a 2 x 2 matrix over Zb It is not hard to show that g is a bijection with the property that g(A, B ) is invertible over Za b iff both A and B are invertible over Za and Zb respectively. (similar to arguments used in the chinese remainder theorem) Therefore, N(a b ) = N(a ) N(b ) Below is a small program that would compute N(n) for any n > N := proc(n) local F,g,p,k; g := (p,k)->(p^2-1)*(p^2-p)*p^(4*(k-1)); F := ifactors(n)[2]; mul(g(F[i][1],F[i][2]),i=1..nops(F));

mul(g(F[i][1],F[i][2]),i=1..nops(F)); end: > n := 6; n := 6 > N(n); 288 > n:=9; n := 9 > N(n); 3888 > n:=26; n := 26 > N(n); 157248 These correspond to the answers for question 1.13 >

Vous aimerez peut-être aussi