Aller au contenu

Le chiffre de Vernam⚓︎

Le masque jetable

Le masque jetable, également appelé chiffre de Vernam, est un algorithme de cryptographie inventé par Gilbert Vernam en \(1917\) et perfectionné par Joseph Mauborgne, qui rajouta la notion de clé aléatoire.

Le chiffrement par la méthode du masque jetable consiste à combiner le message en clair avec une clé présentant les caractéristiques très particulières suivantes :

  • la clé doit être une suite de caractères au moins aussi longue que le message à chiffrer ;
  • les caractères composant la clé doivent être choisis de façon totalement aléatoire ;
  • chaque clé, ou « masque », ne doit être utilisée qu'une seule fois (d'où le nom de masque jetable).

Principe

Ce chiffre peut aisément se faire à la main, mais nous verrons ici la version informatisée qui repose sur l'opérateur OU-exclusif :

  • on dispose d’un message écrit avec des caractères alphabétiques ;
  • on remplace chaque caractère du message par un entier représentant sa valeur Unicode ;
  • on crée alors de manière aléatoire une clé de même longueur que le message et constituée d’entiers (au lieu de caractères dans la version initiale) ;
  • on effectue une opération OU-exclusif deux à deux entre les nombres du message et ceux de la clé : la liste ainsi obtenue est le message chiffré.

Pour déchiffrer le message on refait la même opération avec cette même clé.

Question 1 : génération de la clé

Compléter la fonction generation_cle qui crée une clé aléatoire (sous forme d'une liste d'entiers compris entre \(0\) et \(255\)) dont la taille est passée en paramètre.

🐍 Console Python
>>> generation_cle(8)
[118, 229, 144, 243, 37, 22, 226, 69]
>>> generation_cle(8)
[2, 217, 185, 159, 125, 207, 171, 110]
>>> generation_cle(3)
[58, 222, 237]

Fonction randint(a, b)

La fonction randint(a, b) du module random renvoie un entier aléatoire compris entre les nombres a et b compris l'un et l'autre.

###(Dés-)Active le code après la ligne # Tests (insensible à la casse)
(Ctrl+I)
Entrer ou sortir du mode "deux colonnes"
(Alt+: ; Ctrl pour inverser les colonnes)
Entrer ou sortir du mode "plein écran"
(Esc)
Tronquer ou non le feedback dans les terminaux (sortie standard & stacktrace / relancer le code pour appliquer)
Si activé, le texte copié dans le terminal est joint sur une seule ligne avant d'être copié dans le presse-papier
Évaluations restantes : 10/10

.128013s3o_bcdufvg/0ly napSr1me,(P2=4:twki][5h)6050h0y0G0s0J0o0b0q0g0o0s0b0b0D010G0J0t010406050b0i0x0x0s0v0p040u0d0o0i0*0d0r050m0;0?0^0`0/0t04131a051d0m1d1f1a0/0h0J0k0Y0!0$0(0N0J0l0N0o1t0N0G0-050T0f0o0y1o0#0%011s1u1w1u0G1C1E1A0G0f0d0h0`1B0v1b0G0N0Y0}0b0t0s0r0(0C011G1q010j0V0y0r0s0x0y1A1(1*1/1I1=1E1^1`0-0a0q0B0v0d0t0d0b0J100r0q0R1$0v0v0y0g2f131}0r1b0m1!2s0G1Y1X1Z0h1 0(1w0r1@2c1A1l1n0Z1H2C0J2E0r1U1m1A0t2l1b2q2s2W0:1)2g2K1:2P0v0@0o0-0w2p2!0.2Z1~2$1I2(2*0-0C2.1*2:2q2B012^0s2+040c2|2r0/2 2?0(32340E372~2!303d0-0M3g393i3b310d2)330-0P3g1c2U132I2v0h2z300g1U1{1b3B1e3z2Y142/053G0R2V3p1p1I0g0w0-030q0J0x2b0v0T2f2h1F0!0q0j112n0J110q0v1*1l0r0G3n2;2#3V0(0I0r0-0j290x3x3a41010H0-0q493U2L3144043_0r0h0d483O2}3 304d044f4r2r4t3q430-3$3(3}4y4w4A4b4k4m3{4G2W063o2=4b0I0-0R0j4g4S4i4v4x2Y4a4i0r0j0-0l1@0y3_2f0e0Z0y4Y404i0,040A4^3j0-0o110l0i0y0i0v4~3q4{0O3g4f4(1:4{0F3n0q5j5d4h2%0-4?5c4J4i0d0-0D5q5e1I4{0L584K0-4M2N4O3P5x0(4{4}4H5r1:0x0J0-0n5B4`0-0z5w5m1I5P2`0M3m5M5I015a5X4Z1:4U043:574H5l5-2@4D5,4_1:0d4v2N5{3j0f5D1*4-5T5f0-5L4%5Y3c50525456675y0-0O0K5i5k5N1I5/2l0G56125?6p6d045p4H0/0m3R0y2s2T6F3A1m3C2v2x2t1T1V2v0s1D6I0m3B6C0R0T0V0b04.
Question 2 : opérateur ou exclusif

Nous aurons besoin dans cet exercice de la fonction ou_exclusif qui prend en paramètres deux entiers compris entre \(0\) et \(255\) et renvoie un nombre entier résultat de l'opération.

🐍 Console Python
>>> ou_exclusif(1, 2)
3
>>> ou_exclusif(2, 2)
0
>>> ou_exclusif(102, 55)
81
Opérateur ou exclusif

L'opérateur logique ou exclusif suit la table de vérité suivante :

a b S
0 0 0
0 1 1
1 0 1
1 1 0

L'opération ou exclusif entre deux entiers est réalisée en faisant un ou exclusif bit à bit entre les représentations binaires de ces deux entiers.

Ainsi 1 XOR 3 revient à faire :

xor

Compléter la fonction test_ou_exclusif qui permet de tester la fonction ou_exclusif et renvoie True si la fonction est correcte, et False sinon.

Le jeu de tests sera appliqué à cinq versions de la fonction ou_exclusif, une correcte et quatre incorrectes. (La version chargée en mémoire ne renvoie pas toujours la bonne réponse...)

Le jeu de tests devra donc être suffisant pour valider la version correcte et invalider les versions incorrectes.

###(Dés-)Active le code après la ligne # Tests (insensible à la casse)
(Ctrl+I)
Entrer ou sortir du mode "deux colonnes"
(Alt+: ; Ctrl pour inverser les colonnes)
Entrer ou sortir du mode "plein écran"
(Esc)
Tronquer ou non le feedback dans les terminaux (sortie standard & stacktrace / relancer le code pour appliquer)
Si activé, le texte copié dans le terminal est joint sur une seule ligne avant d'être copié dans le presse-papier
Évaluations restantes : 10/10

.65038.128013.9875s3_8èufvIy |n7aêS1me(P24C:twiDh)6Oo;bcdUg/T0làqAp!.rF-,=+k95Rxé050P0w0D0r0F0V0d0n0O0V0r0d0d0*010D0F0Z010406050d0i0v0v0r0$0m040t0L0V0i150L0p0n020r0v0Z0M0n0/0w1f0$0X0i0w0d050S1c1e1g1i1a0Z041G1N051Q0S1Q1S1N1a0P0F0k0}0 11130H0F0R0H0V1*0H0D18050^0N0V0w1#1012011)1+1-1+0D1?1^1;0D0N0L0P1i1=0$1O0D0H0}1l0d0Z0r0p130z011`1%010j0`0w0p1t0w1;2i2k2p1|2s1^2v0v2x040b0n0y0$0L0Z0L0d0F1o1q0?2g0$0$0w0O2S1G2z0p1O0S2e2(0D2c2b2d0P2B131-0p2u2P1;1Y1!0~1{2=0F2@0p281Z1;0Z2X1O2$2(391b2j1q2}2q320$1f0V180u2#3d193c2A3f1|3h3j180z3n2k3p2$2;013u0r3k040e3y2%1a3B3s133E3G0A3J3A3d3C3P180.3S3L3U3N3D0L3i3F180J3Z3q3e1$3t3(3v040q3-3M3:3O3=3*040g3S1P371G2{2+0P2/3C0O282H0=1Z1O360w383o414a0?4i3r3{010,180?0j413`2~010E180n4v3#4p0p0j180D1E0D0f0L0i0f0w0:0~0i2Q4u1H4j4w2q17040x0I0C3Z0n4)4B4X1|4r040F4U394+4D4x0p184M4O4Q1m4T4C4o4x4Z0x503/4x0v0F3l553C4Z0)3S4?512q583w5b3$4Z0I5f3.3C0L180!0*5p4,135j3H5l4p4Z4%4V3z5g562q4.2X0D0i0$0p5w4@5J0O180%3F0d0w4(4*5q3$4.4:5Q5h3t4`4N4P4R4 5F2%5#5C18545;4n5I1|5z0U5B52185e5`5H3C5~604Y185o645?4x5s045u5)5|5y59045 5`6d69045E4=6p4-185L5N5P6c5x4q5T045V0{5Y5`064*655$185(6A5R5+044{5.4~6O3b6B53685}6l3m6o6Y626i666l6n6X6Q135n6+3$6f6h6P5*6k5a6(6:015D6?4p5K0@6y724x0,6D6F5X5Z4)6u135%4;3o6L4E5,4|5/6W4W6 6Z6~6{015z3x7t6j706*6`7z7w6!6;6a772q6^5v7C6,186.7q7u717M6M046x5O7I4-7a5W6H396J5!6B7h7Y3O7m6U4S7p3z7f7A4!7F7v6l0J3Y7y5c7B6t6B7w0.7}6/7R7H7T4p7K7,7`3l0-7P7=6)6r8c745M7X89787!6G7d7k786N7i5G7?4_6S5-4}7:8w5=8i5_867D7{8g8F7r807j7?5z0A8L5{7 046b816 8b8o5i6l0z8T7?7S8Y7u8l768#7Z5U7#8s7?8.8n8,7z79180T0$1D3-0S4l4h42950S451G0D479a2-2)27292+0r1@97451M8U3$2X0v0f0j0r0,0w0f0H0e181y1A1C1E0n6s4j1T3p0H0z0n0F0P130O0H0L0F0:0(0?0d0(4I0d0D0d180B9R9T0n9W0n9Z9#050r3C0H0w0r0?0$2?4r0n0H2X0j13039Q9S9U9W9Y4J9$9-2R1^130y0w3)310D0n9{180c0a1G0r2(9K3p1N0Y0k2Yaj9E9.0|362N2P0;0|302R0F3F1z2u0D0)ax0n0$aE0i0V0^ai2O5N0$0_ai1^0n0v0s2G0n0rav0Oaj002N0;aY4I5N9+009^1Y2S0n5494040+1Ga 0I0n4Ma.a:a=0wa@0K0Qa}05a 0ob24b8W0#1Pas040l0V0n9t1n9+1pa-a30F2Q0$9+9E0kaJ5N0|2Ubfbj0rbi4m0n0@4Ba 0NbL4h0naWbA1^0d9D0{aOaQ1caT0rai1Y0j0j0h2XbB0|0r1n2X0|b8b)ba0$0|0C1a9I4d2|4p1~1,1.1:4f433b6oa 7?0R4Z020R0D0Mcgcick1w7_8z6T8C5:8I8V8H7Q8J5k7~5m8O8x828%7_6=6Icd6f8T3!7uce18cmcPchcncz4p7E8:136fbhcW8d047x7%cIcyc(6BcN04cQclcR4BcT57cEc!6fb1c!cVc+6 0R6f3RcH93bj962(991Vbn0G1_0Z4~aN0 brbva|0P9SaV2X0p0Pb/2ubU1g1t0h2,9E9,aL0Faf0|4a1u0$bybN0pdw8t2q1g2R0H1f4I0:18090x0U098X7jbO7?dM2edP4PdS0x0z84dW3S0#0n0K1q9;dk0pa-0D4Mai300Da;0sai0Wbr0_b/9,az0n1cbW002u5X0v9k9+0ibu1f301_2UbD1^5Nd/d;a*9-4J0;bBbU0@2RbCbEb|aNaB9Qel1_d!dFd$b`dR04dTdVdX3zeq1q9.1_3F3(bGeAeob|bU4~9M1u3(0^dI1_b~bmc19=1.201/35c99H3bccc,cfcRc/cSct3$cp8B7o8E9o5@7^c?8$3+85cw8V638{7Nc$84cF88c~cM6f0u8f92cc1T43984e1R2(1W440@0_0{04.
Question 3 : transformation entre chaîne de caractères et liste d'entiers

Il est nécessaire de transformer la chaîne de caractères qui représente le message à envoyer en une liste de nombres entiers. Pour ce faire, on remplace chaque caractère du message par la valeur Unicode associée, que l’on regroupe dans une même liste.

>>> message_en_entiers('A')
[65]
>>> message_en_entiers('NSI')
[78, 83, 73]

À l'inverse, on a également besoin de transformer une liste d'entiers en une chaîne de caractères.

>>> entiers_en_message([65])
'A'
>>> entiers_en_message([78, 83, 73])
'NSI'
Compléter la fonction message_en_entiers qui prend en paramètre une chaîne de caractères et renvoie une liste d'entiers compris entre \(0\) et \(255\) représentant les valeurs Unicode de chaque caractère.

Compléter la fonction entiers_en_message qui prend en paramètre une liste d'entiers compris entre \(0\) et \(255\) et renvoie la chaîne de caractères correspondante.

codage d'un caractère

On rappelle qu'un caractère est encodé par un nombre entier que l'on obtient avec la fonction ord. Par exemple ord('A') est évalué à \(65\). Les codes des caractères alphabétiques non accentués en majuscule se suivent : ord('A') vaut \(65\), ord('B') vaut \(66\), ord('C') vaut \(67\), ...

Réciproquement, étant donné un entier positif, on obtient le caractère encodé par cet entier avec la fonction chr. Par exemple chr(65) est évalué à 'A'.

###(Dés-)Active le code après la ligne # Tests (insensible à la casse)
(Ctrl+I)
Entrer ou sortir du mode "deux colonnes"
(Alt+: ; Ctrl pour inverser les colonnes)
Entrer ou sortir du mode "plein écran"
(Esc)
Tronquer ou non le feedback dans les terminaux (sortie standard & stacktrace / relancer le code pour appliquer)
Si activé, le texte copié dans le terminal est joint sur une seule ligne avant d'être copié dans le presse-papier
Évaluations restantes : 10/10

.128013s3_8èufvIy n7aS1me(P24:jtwi][h*)6Oo;bcdgM/0lîàqp.rL,=+k95Ré050N0s0z0o0B0S0b0l0M0S0o0b0b0#010z0B0W010406050b0g0r0r0o0Y0k040p0J0S0g0 0J0m0l020o0r0W0K0l0*0s190Y0V0g0s0b050Q16181a1c140W041A1H051K0Q1K1M1H140N0B0i0@0_0{0}0E0B0O0E0S1!0E0z12050/0L0S0s1V0`0|011Z1#1%1#0z1-1/1+0z0L0J0N1c1,0Y1I0z0E0@1f0b0W0o0m0}0v011;1X010h0;0s0m1n0s1+2c2e2j1?2m1/2p0r2r040a0l0u0Y0J0W0J0b0B1i1k0-2a0Y0Y0s0M2M1A2t0m1I0Q282Y0z2625270N2v0}1%0m2o2J1+1S1U0^1=2,0B2.0m221T1+0W2R1I2W2Y33152d1k2@2k2|0Y190S120l0q2V3713362u391?3b3d3f0v3i2e3k2W2+013p0o3e040l0c3t2X143w3n0}3z3B0l0w3F3v373x3L3f0)3P3H3R3J3y0J3c3A3f0H3W3l381W3o3#3q3C0n3*3I3-3K3/3%3C0e3?3Y3^3!3$3M0(3~3m403T040q0R453,2^413:0q3h1B3j1J311A2=2#0N2)3x0M222B0,1T1I300s324k4j3u054t0-4B464e0%120-0h3P3+3x0A3f4P3@4e0m0h122A0{0o0O0s0d2o4)0m0 0s0Y1z4D2X4Q3Z11040t4U3 4W4Z1y0b4$0s4{4J2k4^0G0x3W0l5a0l4?47122o4-4/3P5c4V2k0J120#5j5d4e4^0D0C595b5r2k4L040h3#5q5l3o120M1a0o2T4.524;3C5y1?0J4S042`5E4|3a4~4#4%5W541?4^585O065b5-5k5X5G045g0B4.4:355F0}5n040X534d5Y040o0W0W2o0N603x4^4`5O5Q3K0L123#686d5{016b693Z0m5H5J5L2R6o40560G5w5a6e015A2R0z0g0Y0m5$615;5?5^3W3X5%0}5A4N6v4e5T5c6k5:3K4Y5=4,5@4/4+0d4!505#6Y6Q6m126c5`6Z3y5f6%6N6/6K0}565*335,5x6l6q046,516J3x5}5p5O5/6:0b2h0401016z7f6~6C125C0Y7a6p5H4u7t405S125V7e6B766M5i7C6l5)7m5-7D5Z6-5N337n7b5o7x4}774 797H6^5}0$7U3a6g040M0E7s6}6a6=6U624G7P4k7I126y5+7L6l6D0.6G6I7Z6:76786.721A7?2Y4z2Y4o1A0z4q8f2%2Z21232#0o1.4A4n4x1G4I7o2R0r0d0h0o0%4(0E0c121s1u1w1y0l714C1Q1L040j0S0l1y0z0l0+0O3A1t5g0l2I0{0B8p0l2O0M0Y0+4.0l0g1k1}0s0o0g0l0U0l0_0l210m2.0l0/0;1/0!8#0g0B0?2O0Y0o0y0J1h8/7+0o8I8S6{0Y8`8#1a0 9n2O0S002`1S0M0s5 1N3k2=3x1^1$1(1*4y4m356d8a6B6S0s4O7.3Z6W7;3o6#864(4*7F5_7^6^6n9S5e7W5!7@4E7_04577m7M6$5h9$3u7R3Z7c7%5(120D9V0}0r0B124b9*5s125v837o5}0F9 6f122ya36;4_al857X879%6:569?757Aah019~ad3xa5a7av6^5A7ray765I9c6t9.2X9|7y5T7B7Q9@9Yay7J7|5.6Aaw9^6(9`4=9:a2a9620Bal4^acaU6laAa?6^0m7)6ia:7:a-5;aL5K0z5Ma~9;7K74a`axaB9}120$7da_6:aD49aF6:7 6F6HaJ6`9_3*0Q9N1N4m0Q4o8t0I1k2I0g0i0:8U1h0;998/8^8%8*8J8?8^2a0m0b2$980z0?2o2a1o8-0E2o2K1j8K149B4w2?409F1`1)2 9K4C359N7~4M9Qal9Ub06!7N516*9#b66?as7oap9-b69=aZa#aG126E81aXa1aoa|0Y6j6@ata crca5HcdayaH5Dbc9+0May7z5U82bhcv9,7Ob6a=3j066P7o9P9Rcu4R4Tc13y6#9#6*aWcX9)cU7ua%6|c)6w7`8L3u73cg8412bQ0gcE7TcBaa04a,c-4W7)7+7-d155ctc93S12cHd94@7`cy7qcAcIda04dc9{6BcFaT3jaQ7Vc6c%abblcRci80bpc}2k0b0q120000al5}9Ad65;9edq9/9(d8dQc@04c_cx5+894u8bbx8e1P9C0.0:0=3S0Jb4123H171u1c0P0+284u5c8adO82bv1Cd?3k1H0Z0o0l0rd_0Ed{4Fd!d~dZ4H8#4.2A8U8+1j0i4.9r8:911%bU1:8+6s0z0f2RbY1k8;1:9i0T91evaMexez9A8Nbyd*0;0be304e50l0b1fbIb*161T2eb48:0 es1:8}5C0m2T0Bb*ec4H190Wef4A8*8_2d9c1S0O2A0le,e.1j2.8R0xb,1K9D3Zb;9Hb@8q9Lb`d!9Ob}cTdd40c0dMc2cKc49!9ma)8u7/ancXcbcLdv9;c:3G7}ch04cjdBdj6p7)es0zc7cn4Z64fQfzcocqfmc~0!bqfrardT7o6x6Ofj046TcXfofY3acZfuc#aqaOfwdefyfp6_c+7Gf~70dx3xbnckdC1?dEdGdIcXdKao12eefCc8f(3S7)e?fUf~a{5H7,b6f!g83Kbra(cd7{88bufibw8qby4x8O1J3keN930=04.
Question 4 : chiffrage de Vernam

On souhaite convertir un message clair en un message chiffré. Pour cela :

  • on génère au préalable une clé aléatoire de la même taille que le message.
  • on convertit le message (sous forme d'une chaîne de caractères) en une liste d'entiers,
  • on réalise le cryptage par une opération OU exclusif.
Fonctions fournies

Les fonctions des questions précédentes sont toutes fournies et chargée en mémoire :

  • la fonction generation_cle qui crée une clé aléatoire (sous forme d'une liste d'entiers compris entre \(0\) et \(255\)) dont la taille est passée en paramètre ;
  • la fonction ou_exclusif qui prend en paramètres deux entiers compris entre \(0\) et \(255\) et renvoie un nombre entier résultat de l'opération ;
  • la fonction message_en_entiers qui prend en paramètre une chaîne de caractères et renvoie une liste d'entiers compris entre \(0\) et \(255\) représentant les valeurs Unicode de chaque caractère ;
  • la fonction entiers_en_message qui prend en paramètre une liste d'entiers compris entre \(0\) et \(255\) et renvoie la chaîne de caractères correspondante.

Écrire la fonction chiffrement_Vernam qui prend en paramètre un message sous forme d'une chaîne de caractères et une clé déjà générée sous la forme d'une liste d'entiers et qui renvoie le message chiffré, sous la forme d'une chaîne de caractères.

🐍 Console Python
>>> message_clair = "Mon message secret"
>>> cle = [56, 91, 199, 16, 114, 151, 104, 102, 232, 58, 41, 84, 236, 54, 83, 2, 131, 50]
>>> chiffrement_Vernam("Mon message secret", cle)
'u4©0\x1fò\x1b\x15\x89]Lt\x9fS0pæF'
Test

Pour une clé générée aléatoirement, le message chiffré sera à chaque fois différent. Il n'est donc pas possible de tester directement sa valeur.

En revanche, ce procédé est un chiffrage symétrique, ce qui signifie que la même clé permet de déchiffrer le message.

Cette méthode va encore plus loin avec l'opérateur Ou exclusif : si cet opérateur permet de trouver la valeur chiffrée à partir de la valeur en clair et la clé, ce même opérateur permet de retrouver la valeur en clair à partir de la valeur chiffrée et la clé.

>>> ou_exclusif(65, 47)
110
>>> ou_exclusif(65, 110)
47

On peux donc utiliser la même fonction chiffrement_vernam pour chiffrer et déchiffrer.

Représentation des caractères

Certains caractères de la table Unicode correspondent à des caractères de contrôle et ne peuvent être représentés à l'écran. À l'intérieur d'une chaîne de caractères, il seront remplacés par leur code hexadécimal. Ainsi, le caractère de code 31 sera remplacé par \x1f.

###(Dés-)Active le code après la ligne # Tests (insensible à la casse)
(Ctrl+I)
Entrer ou sortir du mode "deux colonnes"
(Alt+: ; Ctrl pour inverser les colonnes)
Entrer ou sortir du mode "plein écran"
(Esc)
Tronquer ou non le feedback dans les terminaux (sortie standard & stacktrace / relancer le code pour appliquer)
Si activé, le texte copié dans le terminal est joint sur une seule ligne avant d'être copié dans le presse-papier
Évaluations restantes : 10/10

.128013s3_8èufvIy n7aêSû1me(P24V:jtwi][hE)6o;bcdg/0làqp.rL,=k5Rxé050P0u0C0o0E0T0b0l0O0T0o0b0b0#010C0E0W010406050b0g0t0t0o0Y0k040q0L0T0g0~0L0m0l020o0t0W0M0l0(0u180Y0V0g0u0b050R1517191b130W041z1G051J0R1J1L1G130P0E0i0?0^0`0|0H0E0Q0H0T1Z0H0C11050.0N0T0u1U0_0{011Y1!1$1!0C1,1.1*0C0N0L0P1b1+0Y1H0C0H0?1e0b0W0o0m0|0x011:1W010h0:0u0m1m0u1*2b2d2i1=2l1.2o0t2q040a0l0w0Y0L0W0L0b0E1h1j0,290Y0Y0u0O2L1z2s0m1H0R272X0C2524260P2u0|1$0m2n2I1*1R1T0@1;2+0E2-0m211S1*0W2Q1H2V2X32142c1j2?2j2{0Y180T110s2U3612352t381=3a3c110x3g2d3i2V2*013n0o3d040c3r2W133u3l0|3x3z0y3C3t363v3I110%3L3E3N3G3w0L3b3y110K3S3j371V3m3X3o040n3L1I301z2;2!0P2(3v0O212A0+1S1H2 0u313h3/3{0,433k3)0|0$110,0h3/3F4a010D110l4g3U4i0m0h110O1Y0h0h2Q2z0m0C0d0z0u0Y2y4n492@0110040v4G3(4I0m112z0`0o0Q0u4N3v4K0!3L4m4h4P4R0_1v4W1A444%2j4K0J0A3S060l4_4$4o4(044S0b4U0u0d4t0E4v2Q4#3%3v0L110#584/1=4K0G0F3S4`4{4H39112n0~4D1y4-3s5m4O2j5b045d5u2W5w3O4R1x504V0d2n5K4z0E5s4X3V4K4M5C485x3m5G4T4V5Q4i4;5k4`593V4c040h3X5e4|5o040E5:5n1=0L4k5?0m5^5W3H0N110Y2d5!5U5*5$115T345f61112x5#4I5S6h5=4 516k5g110J4=5(5l4_684}6m5J54564,6c5;5`110X6o3H110o0W0W2n0P6I4J6a6Q4Q040L0g5K0)0@0g2J4f676d6R4L6T5p5N5P6(6E0|5h6,5?6Q4K0F4!5U5E3V6U180b4+6_110G6@5@6:5_6=110F6r6t6~4i5,2Q0C0g4E5 5F045q5O0Y0b5M0d6y6C4.6;6*6b7y7a3w5Y5I526A4w7x3s6w4:6q3$0R46423:7S0R3?1z0C3^7X2$2Y20222!0o1-7U3?1F5V3v4x0d0h0o0$520H0c111r1t4+0=4?671M3i1G0Z1/7I2Q0l2N4C4E1m0l1x0C0l0g374u7J4y8h0b0k0t0*2#0E4+0l0A0l1i8i0~1$0b1/8j1/0t0p2z1U0*0l2H7l0?8l2Q0Y8f8h0P0*884D0l1.0l7w6H837.0j0T8f0b8h0m0*2d0t0L2_0=0u6Z5H1S1s5q0l2F0N6#0C1/8x0^8K8,8U550h0*2Q4z1/8N8S4t0o8v1.1{890P0g8#5H510!8T0l0*0Q3y8}4z8a008j9q5Z1/0U8Z000o1g2Q8%1P1K048*988M2F0g0i8L8v0O878Q1/8g0l0E1n2I2J7+0l9H0O1;8Y8x2n8f4v0-9t2J8y1j9r0k1/2N8V0O1a0W938S8!7w8M194$2#1i0Q5-3X9Z0l0N0Y1g6Cae0mag9t9@938-2d8h0C6Wau8Z1x8K0=2H0`0E7+0b9t8z0L0N5r0m0P642K0lay6#aB0=7w0=2N8H8J0Taf1w7l9{2daE6W0i8?8S8Va70t2_8Y1.4+8+9%3{2P2R0C9N3i2;3v1@1#1%1)7/3V2w2n2p110I0)1s0W1.3/415V33447Rba4i5z020Q0C0Mbubwby0M7n6 4s9#8n4B4D4F79607A6Q0b2g04011faP0E0174040!76bK3va@3ebW6|6D7Db$040x0Sb(6Qb,0x0eb:b!3Vb,b/b_69bXb;bU04b|b*bL6`0J3$6)0b0s11001t1R00c87zbtbvbxckbAbC4pbE9a4x5qbH8d0tbW7B7Lc9bPbR8j1RbVb}6i11bYc03!b^c4b#c10s3#cG7Nb cS1=b=3.cV7bcUcNb`c1c37Cc5cIcKc2bW7dch7Dcacc0B0u0gcf7Pbq7T2X7-9P0Z0o0l8D0g8!1$a@939S2N8Na/0E8S2#a,2z9m1/960@8L2c8S9D14apag9:2o8+0b0r0Y2Udt04b27.d2ad2Faq04a40ka60L0Q640W1Y1/1vaw8u1wdBdIagaT5qa19J8r1$3X8Yaj8=a29!cr9z8hdp8Zd30Q0*8/642L8a8G4*1w0=c^1|aCdp7?2Kd=0l3y8:aydf1xasd?ac1.8Odh0m0ba@aG8za!2d0E0f89ay0.1.8nd48W7l0E0b0*0udE3=0-0/0;04.