You have probably heard about one-time pad — the holy grail of encryption. In principle it offers an unbreakable encryption algorithm — each bit of the input is encrypted (using XOR) with a corresponding bit of a one-time random key, and the keys are never re-used again. There are two main reasons why this scheme is practically (and theoretically) impossible to break and why it is considered to be the ultimate in security:
- Since each bit is encrypted by another bit, there is no one-to-one correspondence between symbols. The first “a” could become “z”, the next one — “U”, the third one — “3”, and so on, without any pattern. This defeats any kind of statistical frequency analysis of letters or words.
- Since the keys are “one-time”, each message requires breaking of its corresponding key, unlike systems where the same key is re-used and breaking of one key leads to automatic unmasking of all messages encrypted with it.
- Since any message is of given length is equally likely (consequence of #1) one can never be fully assured that the message one gets when trying different keys is the right one. If you have a six-letter cyphertext “x2YzB_” it could be “cobras” or “stamps” — there is no way to know for sure.
The real problem with the one-time pad setup is how to distribute the one-time keys to the other party which is meant to see the output? It becomes even more brittle when multiple addressees are involved. It does not work at scale.
However, during the Cold War era this was actually used by spies who would secretly agree on how to derive one time keys before being sent out into the field. A key could be something very standard and public, like a set of poems by Edgar Allan Poe or the King James Bible (not really, but more on that in a minute).
Let’s see how we can play a bit of a spy game and build a minimal one-time pad in the browser with the web’s lingua franca — JavaScript.
First, we’ll need our encryption function that takes two parameters — plain text and key text — and produces encrypted text.
For the sake of simplicity let’s assume that those are of the same length (you can add your own validation or wrapping around as an exercise :-)).
var UTF_LIMIT = 65535;function oneTimePadEncrypt (plainText, keyText) {
var encryptedText = "";
var i = 0;
var j = 0;
while (i < plainText.length)
{ var p_char = plainText.codePointAt (i++);
var e_char = keyText.codePointAt (j++);
encryptedText += String.fromCodePoint( p_char ^ e_char % UTF_LIMIT); } return encryptedText;}
So, what is going on here? Basically we loop through every character of the text to be encrypted, grab the corresponding character of the key and XOR it. As a final touch we take a modulo of the result with the size of UTF-16 character set — namely 65,536.
OK, now we got our encryption function, how do we decrypt the message? Well, it turns out we get 2 for the price of 1 — we simply call the same function, the same key, the only change is the input — encrypted Text. That’s because XOR of anything twice give you back what you started with.
Let’s try it.
var encr = oneTimePadEncrypt("This is cool!" , "OneTimeKeyThatYouWillNeverGuess");console.log(encr);>> ���'I��k��;�@ // \u001b\u0006\u000c'I\u0004\u0016k\u0006\u0016;\u0004@var decr = oneTimePadEncrypt(encr, "OneTimeKeyThatYouWillNeverGuess");console.log(decr);>> This is cool!
Notice something very important — even though 3rd and 6th characters are both “i” they encrypt to completely different characters — Unicode “\u000c” and “\u0004” respectively. Spaces also encrypt to different characters — “I” and “k”. However, one can notice some disturbing patterns which become visible if you output this to the console — spaces tend to code to characters, while letters end up being Unicode characters. This is not good, it allows one to do some basic frequency analysis and possibly guess words.
Why does this happen? Because there is not enough randomness in the key — it comes from a phrase and phrases follows patterns. What we need is a better random distribution in the key. Of course computers cannot produce true randomness, and JavaScript Math.random() is a notoriously bad example of this. Thankfully, some recent additions to JavaScript make the situation a bit better — with the use of the window.crypto package.
Let’s write some code!
function generateOneTimePad(len) {
var randArray = window.crypto.getRandomValues(new Uint16Array(len));
var reducer = (accumulator, currentValue) =>
accumulator + String.fromCodePoint(currentValue);
return randArray.reduce(reducer, "");
}
So, as you can see it’s pretty straightforward — here we pass a length of the desired one-time pad (which should match the length of the message we are encrypting) — which in turn is used to allocated a 16-bit unsigned integer (each entry max value is 65,536 just like the UTF-16 char limit) array of the specified size. This array is passed as a parameter to the window.crypto.getRandomValues function which fills it with random values in the desired range. Finally we convert each numeric value to its code point equivalent in the Unicode world and concatenate. Voila!
var payload= "This is cool!";
var key = generateOneTimePad(payload.length);
console.log(key);>> 춛邼ķ뒸裠ﲹ觜䐏 // \ue78b춛\ue9bc邼ķ뒸\uecbb裠ﲹ觜䐏var encr = oneTimePadEncrypt(payload, key);
console.log(encr);>> 췳ꢵ邜ᦤń뒘袏ﳖ覰䐮 // \ue7df췳ꢵ\ue9cf邜ᦤń뒘\uecd8袏ﳖ覰䐮var decr = oneTimePadEncrypt(encr, key);
console.log(decr)>> This is cool!
As you can see, it is no longer evident at all where the spaces are. (Some characters cannot be rendered so they are substituted with boxes like these — but they all have a different code point which is inscribed inside the box). When a longer text is encrypted this looks even more random:
Ὥ桫岹킊�ﮏﰷ탛ֳϭ掓쇑죊ᧅ鷿̽嶞鸝ᄅ庤�᱇㔲⡝癘ᘱㅠﮛ鍧괢픈캞隆↖蠨ꅑ將랹誱黔䲍墳笴榋艜鏰ﳯ䴫偃�奨셼₇纋哴䡪캻젇냟䣐씒ժ튱苅ậ暔覶麆簹䱌≞맣䨌죈姻觲ꔧ歽잪睚خ饣癓䴪爉녣룯䧱㑓쎫∳뉺噒짔噝鑋�䕦ꁩ쳗ﲮꤿķᏒ牧卑�ⷖ淽悾㠀顙�㡝⓬携Ű밑珨椃ᝋ᪔㌞隴櫄矇孃仑딡⬴�ꒂ䐏撇蕠⃦橄䟋䜿୦ﰃ她퍅�嚄♥࿚�騙趌䀎ᴄ럤橤杖樲滜㻼ᣳ뛆牟ያﱛ仴酣墚다㠄鋪蘖ᆸ玲◺叧궠磀栭桜ᾰ宮쀓�穮䯏릉퀱䝑榹戎巏탛暏敇齭䂳颭草叭蠙ㇺ矅欤។胉ᛪ㶰嬧�毶屭㖱䄖ꝣ㩘䪋翔拨㖊耺盓绰Əധ骼쪤⯎렢ጯ䩅娱㙁籠旅䋶Ȕ❗凎ꋣ᧨抏眢ꈐᖮ뺢ꉐ﹣蹉�嫤⎭횠ﲐ�秕媫凪❆찵⠋칂䴁䰭ᖥ⠝候 ヺ릎鑘軲숪遪鰢袅㘮ᰧ巊궋տﻹ᯽拡㫻痙।眧ঌ围뱤ꥼַ㽦똝䜸嫥泜켮�娾苵ᜨ饕ງ璕ō⢻ῇ顛㈣䯳迟�諀睟鳸萆ꝁ뿸冝韲艎썼ꦩ㸝�ᱧ白�鰤眈趇ਸ਼ꟿ㰞熯�⯾숈㓏꺻쬲彜월渀Ƃふ䷆聒騊䇒臍䊠柺樇穅䗐↢랼ㄼ켣偃䰣�軹Ƚ荖㝣ຼ䎘፠Ⲏ셴ᖅ꾐쟪੪蜢꒙貭쨶뤗߸㓄鐑⋍⣉〔팫�퉊᐀毪햖孚ጟ誳턶ᆌ숢놷粉跸騷庍곞촉砭棇輂䚴ዋ듿騭꼑䴎㿊五ᒛ㫌䀜죦꺦ᘙ嗱鮪椏ꢂ罹烿뤍鑱ᶕ꺮艴⽻饚醈붅쑠軭쉨翍棉샱饢蒉냤湋ꎝ윗㨾ᮉⵘ殉흡㡒ѱ”ᶫሃ봻꣨籾杠ꃺ즺쳷⾤⎺壉屉㸕뻬竼᧸앆ಈ鏿슑훓斥⫲䎪봋岬ƻ鋞鵁鑖찱仙셛፭⺟䦟ꄵ쵀ஓ█訧꺉冰͕㶑᳒㌼ꁋ版哫ꛃ軐㭓鍻甬瀕╝䃌ऱ钵視鼈缘抴勐ぱ�ꅜ瘠쯩峱ᗽ쯛㜋碁ῧ麫秫ᓙ✟⛴㣘꠱�獎쐦趕䲯ⴏ焕ᒎᡮ鎴﵊쪨췻睫㑼隼릈�뙻웲ፆ뺟�覿硰僔㢆蛹�鍺璎⃣ꅊฅ⸤፪蛽癒眦୩쮿鍓圲膕軾軺蟱诎驛짨�踃쾇ᘏ꩐﹪矊俛璵∛嚥혱檒皢壻猀巐�熃飮韇彀푮ᆙ᩶檛�峨䏦묎翢Ù磕欺ꅩ갭쭏逕䈕辜䙠u潱롡蛄帵礲ꌰ�㭰�휯杫鋛閩麼筜銢雘댯�赟థ흮鳇굱恅泝鹅몆狧䜺봢綒⛅謘ꢲብᕦ킼㷄�蹂礟鳺ጱ⼋ץ�ꇋ긖顯趮쌌溨䠈샞䠫哿쭑冭绰丮麞䲽䘦藥粄ﷸș탵ـᐃ뎼돝褘亣諢ꂎ읗眓䵟撸说幸퍖痈킉绖♢距핖烚뜣烗ꃭ䟢햾혢ꔯ缽웲辞◀劣훎ພ艌㩕뱱华蹳纵佢鐥惪쁯劶뙽ẉ糪焴䮶�䤌失䏜槳皖㥠⛓輞Ƚ䅝瞦䛑ꁕ⋽㉞嘊婻莺�很乐嵏꙽哣�ᔍ䤼쾘ဒ�澓æ纁먭孟马䰎䝠슳돬민櫔摾Ƞ겢㈑眭쒩퇇飈⼃뀆Ṻ∔ᄱ㇜ᩱ윐뺁伇貺딑ꄝﱘ䖪鮏䳷숥绵翂쟕敉珞⁊䳣虮䁕鄆䱌ј�齭��關⌺ꁾ㰌提鴋郫冂㠬閴ꄂ〉ࣤ爁瓔㟨钟篗롑뭒袢೬똝ꎩラݕ휙᪂⓲ڸꥭ搱�ᙏ㿽춫◛덋넬쮀抒탼컝䓟ᥕ띵횵押꺫랦䗤∷R퍶룻䶊춪뢛㳭嵓쁋㉿�ᠽ牯㔅먵�쇊咑ኑ⺒龊죤懤ꁠ옯혟卼硏嵲䞐籜钀ⓍᎰ뗳钻䛑ⲇ蟳셵쁂긠䌿售㫾࿋삝糣揥ℵ楑闦蒎麈䙴�㾆穀贅썄눅䌊ے뿪ꃳ誇뭰찱ࢺ佌障묳ᾗ郊બ픷䓀ᒚꖓ锧弜硥찿嚀飂뱭ᩈ㹔⸛爹鿍焗灩趬왣皊⊮蕆Ώ٘⫝̸㡉넝ﭫࢪꘉ罢痀톺䪤蹒䰜む腭꧘륛麰蓑⦨餥刱醲Ⴗ㟟⿀헞✑찀ᣄ䉣졢每붺픳ꇶ䌰윻㜅讘헁㎥�䷩㨆쩁楱쾒ኒ鴲勂ꓴ筎敛约ힽ䨻Ύ㋙ꎠ뤬댐缠Ꞿƙ븵㎇轛먹꿭纺ꩼ䕊ᥫ很訚䦚珧홚獪漣膨ꀻ㋯맭藖戁虔笞硎췡뺄讽슗៎ꬮ䮕闫_汆梪堑ᄬ㡶팪�邇ꤩ䢇䂅讍绕✟총ሦ뻪聬ฟꌗ뛹흻✜〒⹅鏹녇ユ圉翓∋锢諭邽삉葤ﴭ︁⢌ꉏꆻ쒄괩崴螞䞆鮯ङ냺�쳩얫뭽⪙髅ꂥ둯ក�᳡เ뗷◢蝘⸃鉧⣰겓䄬啿�缥鋈赐헜ⲏ阐惼⦅桞ᑊꚻꅫ廆街숥崅區肤㿆꼪녲俘ફ鋀ྙ닅�캗ꊢ⌯炈鿉鿚怢拂詺�쯫꒦䡍ꚪ殓ᖟ숹�嚨㋽θᠡ蚽宆ὼ뢈⯥ᝥ詺ᕻ樌ퟁ�䫿ࡡ澺鿱갳햱鰣ᨧꃚ声ᮝ㵕譬쫄⊢୶뚲쵰塻彐ꐚ뽉ᆅ㥵똴猨ᨷ㼝⤇☋嚬愔ᚴ恬�띱⧧횙ㄦ릹䐚曤ﲄ洀꧀푰발鍢䶓ﳧᖖ올근聐哗婋돽㋪먑쑎ř᩸ᾗ堋ഛꕑ⌶嬾쾺졡Ɛ슰诇ꡌ惭棇麅ࢨ긴pꋀ言�ꦦ鰸惵㤈襶豔魵붚贬䛐䝂㬓층�錇璩㢔�ᣓ㧮뇢NJ팚蠣窙DZ鰯磀䭝깪냱분䵐䷬봮�ꃘ冻ٺ㾆쥉㠟김젖冓䰊俩ᪧ∛砻鶆闘㏿臯㕳隞翕�诠ꠄኔﵜ怵ࢰ⽡鰜蠲ᅱ�፱ˈ憯憓☇嬏ᇘ淝ꔅ䕺㣟蒤綈ᡆ欷쿕꿸䥻⪨ꆕ徣댒ྞ㼐䰲怂麛ཅ콢꽇ى蓤疮�骜쟮シ猻�腻�壱⻛鍏⺵췧퓝ヌ겚끭ꓨﱈ栬瞪衣魐ᦋ撳麳દ鎟胋䒠뉩弟꩜䲮롮ꥄ髷⽽ि蔌哧鷸̷뭞ꬁ琨얂ׯŦ鉅鎿⻣ᮘ昱绑礱䭩䑛恎ᮇ秐홍쩶訣㦅袀뇤唁圉ǻ࡙룸覹寚၆隘埂鄙嚎ύᦰ湛藝άηꘛ寗䪏勦ဏꁒ辿鰢錭탵楶뛉꼵狞즕埴纓饊떰㒽ꪥ樈ᚘ뫁禹蓓眧�㺪䭬쯪ƴⳋ뙑擟髆㠍㰄硅恐ㅯ⿕죍㳔뗕홈숨삢㢘繯䅊䐣啡轓껼࣒졒昀鰆르릮ᬲ翱퀠畐箏烁⧴탣忓嬣䴻蛋塙螩䑛푢렐䙃辮셅梗▻挥鲁杞忔戫ꎢ�᎐롅Ⳉ㒩꽦媠他䜟㓴ﲬ⥆㽹◳꿭⪱ꅲ톬岿밳간ᔌ㲧糾郠蜿鮺ᘑ舰�漢劤常놩꒽牗滝夕᠇䖂傰驌䕯喍깊폠韆軏ᔖ욝輓듹臫⺄∙瓦�䢉엯渍뱗슻尰糦ꑲ蠕鄑�ᔐ�ퟺ㯬ꟕڼ息孇䉓゜ᗋ䶆ꞃ을唟㶶錏툞䊱䳹쇺惌ﺴㆶ◓裡쬊ꕛ譿斻휧ꢻ⩫罊쏅¦ﳪค褛巑ꕉ䲰槥⇯ㆿ㓂샟궉袗ℵ쵅笐瀼뒤ⶩ蛒渣欤싣䳱멂萩矙鞬⵰❲퍂ꡱ꜁䤟鲥͜떆棪赛连垼珹辸諒睊櫌杆嫠➽㝦㿾橄�警姫谒Ƨㆥᆀᆄ享榣⫡و苲䏐製땚식糃ↈ蚯昍㣇ﰫჩΙ暘ᪧ蟕쁊ꌩ浘鱇䨭硍墴句⭝鉟懶勂圢ो绌狠聰�薻⅓饽齚断ࣶ姖啒碟᧟쏰㯩㡕壖庯㨂ꪢ燅⍜驆嵌漛뮬ꀦ瞥劜ꋮ㼤닲뙺氷䷍骿쾁㍐矀ꧪ뱢感葳틪䆂�눎趯託ૂ架৮㚗졫둢㊢䓺곔ꖋ嗻⏔ᘒෘ춮搴⃣伸ឡ藬勮쿍걨雉仦謜邊녺�挒섙胔꤮�떭힒탳饜ꋉ樶气㮗Ȕᢤ뮓Ḫᢐ跮峂蠶耨ꡜ䨘䂫蚛跭⅖仇뻎��㚑쌷Ẇ�䬚Ô㏸康혰灰줹츢砶뙓⌿䨟䔳㽘㈄諤ď鬤己䡈븶�퓢ធᤩ␀뎲ᔡ谘¡᷀⹑殕辏紂炫鰦Ƶ錑稹毊䈎⨉¹籣땬⬴ᖅᆓℲ鴧︇趸ᄂ�켯㸦�쭌軉鐲侂ﻝﳫぶ�ꀎ빤㫞졷줙迍茐ᒌ틿缔溦싐镒면곆ꅋ똧쟓끣憐슶쮧⇿뀱渉ꂴô戒甙附ꍗ餜㽚甔�ၔ뻸蚔縛섥ꃤ贀霹㣘滩隴碧팝⒗ჾ⚥Ʋ䡚郀쨴㼭搘踤첈⃭�뗤�윷䫶䄦鼑�뉷ཚ備섄큁╜ㆨ寊�㯄녃裘ꗀ쭹ꯋ柫羴忰觇Ხਓ蜘揺豍뉃ዝ뚢�圇麭ᵠ⺿竧�䵙ㄸ䓐⸒孠ᴁ틁࠙ꧼ✰캍ⶀⶡഥⴙ箾峉빹盶쏨핗ݤ爍令ȴ櫠鯨䮹ⓞ乗欌๛繮䅿ꈇኚ螽茅녂읣ᦫ�秉ʿ靰뛜揲鑇ႆ敬彩뉦䫢ﲠ齢䙋؝縫鱙갦単폚鞦䴔⥢閹ᙄ閗쟵ʼn暤瓁㬝箼尕瘆푪ᴎ筛镢켞Ს㿺碮禠鴣㰽菭郥呡慙義墐⠿쪮麧�钊쎎䫢䀀ꂅ爔횬坢嵌智퇜䔲곆煛뙍앜⪡Ꮶ钗ᙲꤽ⦀服Yꥅ谂洿꾒�㣊ؽꀰ鑜궷봄ꡖ秵≉띘䬾如娼纝쫖鈆镂찑䜢긴�忩嚀萜麤ّ殈ᦳ媝톜熀氓•ꛗ�쌃룞⿊蹿氇륻婭专ㅂ⧠뺙揈偲똽⁇늣侮售菒혵㝩ꕴ戳ಿ�鍧㧏梽쀔禒蕱訴末⥩ꡮ戇䗕闙≸绰㡬瓖ꏮꠢ巇雸흓뒧ᢹ稡醨缝䯱ﴧ똢綢쓂凡緽繁뒓蜵ᵷ垌菽睧戎ℂ㊍湻붆함ᖒ캪傫⫞蹲흕�豲ׇ咮홚࣑硒㘗ﶒἒᯁ鰯你糟ﴷ鈽筼뎔딲⚲䜍颓ז㭴궒ⴚ⨓됈囲竭켪扌魡ࡢ喑袖桤폍묓ꨨ譭絖厑苩ⲕ슞ﰭ鰖腞湣ᐗ⟲횒䖘⫻デ㱯往䁯셪区䘕�હ鏡跠Ტꂘ�⭭繖︌鱈춀믧翧ꤥ⭥⦇ҫ憐ฯ靬鰌瀔혡䅟䡇遽䊪垦ꏐḣ뒑㹮瀞銊뷖⬑ᅶ⡮䱞檓⿈쵂절吇䄄‱ꄅਠ夘ထ忀흛識鋕㺖廅琦ᚠ䄯笤丁삀ꯣ⊷☪㖙ꙴᬺ誇㬟阹裵⬢䡿족㍑鮌�쭦赑鏮윞䇜㋴䩵薫䩳ℕㄗ⧲찌婚芀㧩隿櫤钇ꨄ挶᧰댬捳疚驖ꓼ닷齼磒�픖ꮎ鸬彠䖝ꪴꇡ䏟郶恬⸅㞏懍鼠卾㶵쎧됛띴ݍ珎肀ᡟ饈剽똈撋㑄㼕瘚폪淯떁괓㤡촲涋ᛡະժ㸳ᒿ颪䦔馃欻ᕘ㓪ᣬ铘࠙푼٨砼潋㔛乓夿ퟔ켉겭卯⍏ᑨᝂ猐�鵖ᮚꋀ젿猡㐷灣굉潎젢㉈꤀Ꭹ힅�製嬉㓐⧱ᆼ䵗䒱끮㗼粋哂⥈⫾庛᩹Щ쉑꘨潞价⚃閿㞊䪠뒄뫾瓔팀헲덢nࣆ溾妊땐�劸ဦ赅֓毭牍帹ﶟﹺஜ怸�繜ꅸ뙶⍗퀵鈓柅ᇚ욱⚻�돗瀊甙兡껑霷黇겠�ꢴ�ꩵዛ行䠰쵥ଉ氿�蛯ॹ�ꭣ祭⿓廷톊蹄줐ۓ撌쩗誄炔囷ᅁꋛᣴ⦓Ỽ픵썈Ⳬ햀윷뺦볋軯棼鉠⦲↺㴵迯얷ꖂ妡太鮥䲯Ϗ孯伅㯕騢ᥬ⾂핣ᦉ⺁퇋䳡鞴ꢷ줿ꠑ泮⎱끷띦힝랴쑣궙녦⇑챋뢔腸빕鐫ꔌ塔�꧹䒸䮂迂ᒆ겥䛺퍗饫న浳蓮■ൈ匷㛣輸若糧ルぜ꾤ꋠꂴ呈纣ᶺ繁袁弮칮ꖤ픰奔鎞ﭥ컱峥թ�䛤ﳴᰑ黒ꀞ釷㔨К쪞쟡뤠�嘫쾗坰ধ뉻㾄聶㞙爀㖧얆㚞撋풦䠳㶃芪揗눂ݗ频䍺ᬡ沤瞋뢳띗⭾�Უ䢇ᖄ䫖큙쩑憏Ꮼ摋�ᵈ䩱왎ퟡゼ씲ॱ✋䦌ᅵ跼橽裷⥻僦툢ꄸᚷ꺳艫覻ꋨ撕桱ퟩ㏜籼猳稱ಾ轾휷暔〢Ὑ灣뚬덝䤿獹詸痞缲파箈顊ܸ⋯沊ჰ媖݈῞墳젏ﳷ傘킳锭耷ﰘ줭䦟ᮥḂ⒑뾏ᘴ늖칮᭙菣곢ϝ쟨蜅⽫䒏볩汾攅둵Ꮮび䯷遰⼁篁摀䗖⏙単‹ᱶ셅컰ᔺ䰧褦䇣⃘ꌰ꒤䞊ஐ⏮髶쏵緾锆엚懵ἢ둮❊Ҿ鵷졸臑鹎嫹읹ꃍ鞒ጋ꧱ᥒ쒦Q䴩䐟䷚㥤傭귪鄍巹䋧蟟珽�ନ犿祒쓨㷳㵃놆뤕쓫씄橌ڨݦ폍�쭿�Ꮭ篜㻮罁裝ඥ죑뿧抦퐛꥓齃᪼鎳硦᷵刮<醑程嫣�洅偌ࢪㅠ麹㓺怗化珛ꃋ䯾濄쁓鞂�䡻榅⋖뒑헏㍖㍐뢄ﴔꈼ譎筋擱స洶鯦▖댲ꀌ╂篪릆䥪⾇㳌韗㡸ᨇ絀ᄐ⯜Ā薞믥愺�㕎剧妼豹Ⰰ牻귐ᴐ暗焁㢸됺ܧ鵼赩갎ু枧䰴퍛䁗介݂�ﷰ᳤톴�举ᭆ查䘠鿝尫旴뜔⋡ࢌ㧙䋤헊屼䦌랸娶舶ਇ�둤䖋銨⇈鱀屸칏荎䫀⃚蓻虜덅뜅帬═ꛪ㉘㶏莦跡쁵ḡ쐲鵑㗺鏤弹1ࡃ㪼_呄쪋멏�찺銲↓坙㺁톄抛⑸ṋ镶헮鐾쇆苏䪙臈愆詣ᨌ㽽췫須䋺쒍ẝ㞼즚␍詐浡讯豃轉햐؉绀䭢加拲ꪼ␥䏀�調�⢈⫑튏ဦ鐏營�쀉댲邆苭ҡස猧䏬葞뚣鳸�睊艗ͅ─⡀≱附湁ŧ⑱婹莺鈎敐懥唹녔窢牜㶊驳켬岊迨㎶푸Ↄḣ蠌샀옣戊뺶か몒酯톯嗽렦┾萒喅㙻쌪渝䶺ꍩ툭흝ﷸ顎ⅷ믙믗꺎渶洖皇쇺钺᷀摱厮叽蘕✩곙뙛꽫ﮭ⣃諒던䛈硣쾕촒槠흼ӈ㕣轏菍楞惥ﳦ冥㬖棑ꣻ碠阴쟆䐁ꚸ䪷啯帖碮穦阆ᗖ失纇ઑ䮫�㼅솬⒁崦⻖Ǫ돯ꕱ驆壴⁇㠆ꠗࣟ艳뎹唱窤虥�ჿ㭊䆨脹갺׀ꤶ쁶⪧닁줉嬥蚥ꉎ短瞧�ﮥ榯ϡힴ䐌殹珋횷ꓺ梲貃鸤ﱜ㐯鍗擄﵂役魐輄索㔳씰潹捭➦ۄ쯭໒楷그膇隳䶠菋�紫ᑡꞁ賝炠Ɔ㤘쀆넴䝝뛈뵶錒䐽ꖩၶ떓ಒሟ黴ꨇ捶䃼椲쐹麸⢭炻ᚔ點藱佁뙓鬢릅暴콜沃葤䀁슠䟮릔ꪗ餠岁㊼⤖ꡢ欓璡쓝‟òﶌ衜߫龨蛒夯㎼橡⢯끠骳猛䏚퇝굼�Ꙁ怑履ᗇ鞎ᕺ孄ࢿꁩ뻋쯗菇쬝믹誄硤䭢訩�ᆌ襹ꀴ쇨궀滇륯旆⤇ꃚꈏ㪞䜲ﰞ�㸓蕞틢☠巈ⱴ掲威⌼悸⪴哀햰䗎煆ၵ柏卜囗䑋羑尌궗㞀ც眶ⅈà蟾睐땨퉘奔养袽⫥씜�̸즙㮜ﺬ⧞忯�뤎鋁逩ࡅ륚⹋촓씍逭⅝꺌祿댵栗⽨틒อ憭㘕쳝䩻�にࠆ泾Šꡧ鑿븑캁㖕�⫯x袢镽ꍙ鍦谧䋤簂棤㷅᥈ܘ뷤曋轨ڑ똉ꖑ룏䕜鲲ﰑ뾱궊㸮폥ꡱ廮✺Ⱪ㬴口鄾哹㎤鮗罼ৰ饝뽳⟣潺ᛥ唻⫃満�얄흳碗糘솜찺再뾻�溨鶎ꈀ⤒⤯䈶鈷炁읈╠蒶䇋壘㯛쇽脬㞐ꆝ푆鯵鷄愩웸뱧梫㑟尬惽끰羰ꅀס叮농ꙝ뻒湃꺫정孵筂拱뷭僱櫶䄓珊둥姲艬侃♥䓜옴䭅䍭墺鑊㚼솮岐氓迧︴ꅯ㍎뤳ﲚ믇敜�饌궒ˍߧ蓮괷퐧㼘ᳯ䝌㿑䝒㰳㖌䈝ⷤⵑ밍흫↫⹒筗❘ᩅ䏔䈵䲵ܦ轪빿ᤘ�쫛戓퀓�ⷸ슛ỻ饷꯸㛆栲⁀ၕ싷᷁윰맑諑萕赡쑐绯ᫀ逳ꨬW퇏谐琠歚혒c끲㓲ᙛ╷唍븹믮�ꀳↄ冟ᗾ姾睨♠ᰊ⏟랰沺饕완⻭㈍솭叓覠脪ⴍꃈ᠍崓ῢ諫蛸瞨쉱㓛غ♾誆㔈䲢璺ꊴ湪✪ﷄ陰荇ٖ芰걵옚ݩୂ켥ꪉ㦑䳷Ḥ짳蜹魣ꮜ✪˒釁柨䵧쩗鸾鰾뉶뼲倈氏爗ᅏⲖ亪풪楒�㋼臊樰䡹盜餼ﱣ皻沀⤟캇ꎆ墥彷꒬뷵શ1峧窝ꉞ姳탼瞃줕燤ꇝҔ볜㡡厢춃죉ᴥ붡桧봟㒶䞨!⪎깼茁둔阔떻拊衙㠈媈ྸࡉ૦蚯ꐹ៖딚꡵㦟佸ᱤ떇뭂濸⊚�쇴㰙늼毩㝛腄捻暜琴പ絟皱흿䈰됈鯼똈�愋❯홇⎬㘦倵䗡괐邱㜌揰䏖쪋Ⱒ턝�拆븐办铠䑟ᛏ䏍䯸✐ᷴ煃陖濣夥踑▅ꁿ敺��Ϭᆎ㤋୕굞缇䴴筌ﵝ熏隊�樜豐␊�딮ꕆ뻋ᕬᨣ䏫꼼룢�ᓼŵ㗵Ҵ탵纟㨭繣驌䉺缄ﯧ縸䮇臵涰갇앴ᠦﺙఛ葘⭛漜⛪̡ꋁ఼鯔늂얩ꇐଁ륄鿮ǘ弝兊쓯�ᭉ朂傖襗决�⚳⎐辚暹䫆췻鍹剈ꛭ쵕轳⍤⹜皁륰癊걑㹿㟐潥ዒ𥉉仄䯲勈讽鴀㆕ሁ칻ꌪ璖挀求䵋ᱸ⳱䤤╫㰱ꮱᇵ扄闏떄➄体ﴜ宬⃀浹쨾⼉雦Э홉ꎘ煖愺췻䕞℡䙖*ⳬ᎖蓍ᔙ쬞䊄ꔒ橺ࢿ䷞챻愢햡堧註彄瀖绎덽ᅳ�膙ꖶ⾉ᚙᨶ迣�Ỏ宸⳹�葸詬ɒ혍㾱㉵倧醄לּ㤛豟ㄝ紤躯⠣瓼鞪蛤᧒�皌넩湏↷䆻樃⎿蟌흇⓵爪섫儠㘋ྦፓ螃虠딿뮏놼뱹ﲻ깨젌锵蝎꫁喼퐪盫䡏뽣�ᄿⱆ푪俬쾵耫凢踏櫶펝㑨흟犡䪊憴ꚯ䲂≇됯ࠚ༚룱㵥쎧褎錶屺帗砄ꌐ츧俼㴂笠㠝⺷䠉걻沏䧝鯤鬙哃탒̰䪐愿蠦鈦縯䩫晙㳧�▞釀㢓詺骍됗쪫豈炖Ⰸ締壤뤡ૹ꿸쬂蹦堢沿쇬䟟㼫喎∈�肁숙С놈✎ᙺᏇ녢뢆伫瑴醟墳뿠坹괝蜌�놓ᝤ哜쯓 싋닃孝抻耪餾邏儨곘ࢬԷ锎焝ῤ㯸♈薧诟Ꞑ沊讫돜镔䣜앮脅쇕戛鞻媇홨靻嬚ᐂၠ숈嶭奆㛤틤睊陪㍁牬亼腅ꐭ띶ﱺ�ꉰ��䣙脳ғ㍇ﻈ⿂硔떨鳒㫤똤ểꀸ丈ꠉ䲯솅凨컩씶㯪埿똘쵈⡄⠢恹튈�멎裲忴䐸Ḯ�齟蟃騛�좢厹끷㧁릶䗅뾂ꗙ䋉퉕뽑倹᳕妪蓋붓榺䓻ⷂ鉛ꨎည썶ⷦ蛕ꦵ줭⛚뗾쨘엿ퟜ깂ꞇ�鑯塼꼆뗢挙ྊ뉰䘦Ä꘢ûۯ畃♒숱黷柒칮袓渼ॖ挨琖懈镧瀴㕄蜫訨옃欓ㄒ齦ᗼ肏롐婒큱霧�㵁爡纻憤蓊ꤴ횉ྖ䓇恣끛꺢㤲࣮榫㺧ﰞ㰘鰯墳鎙䭾峗⢨㘴矄ԓ↓蛨ஸ᭧栶ꚤ�ㅍꕒ쑡괃벱荩䲉킸ꔴ坟ǍT䛁쵇⨕躉Ⅿ쿉脐ﵫ竄⯁柕듒唏ꊚ㌛ڛ轷⻨⋐鳾粦꼒輖�롊㳇摹〸ߓ멾꽐뺜唠䰣졹邍㲡鋕李䛯⭑裚혟옹ᗡ雄㫙婞싢옢⫋錥碪⬕ﰜ䗇礣府ዥ薁铏쐜榜놄轜籲뚃旅關䃃悮�毺뛁䍜ꗌ棬ꊄ翏烸搜돘⫽촫꾶嘔桾죷㝡�᳅悰湷筞ᑯ塊�땓딌忌즲砭쫊⹐䁑曆븱ᾄ뮊앾耷읜璾榴➈혋䝦នܳ䷱ࡾዎ梤㈹푏챺硚ꂭ쥴⫁᧥䰵狧㑬ﳀ떛ᙑ㫮鏇ంዩᐣ㺐䀘ṹ瞑嚠柎䮐䵉ﳖ䰧୴鏀�쿧☞ധꏷӈꜬ줻뱦騵ꗛꝃ艾齝逹㹾교ﻖ건宇홞◖독ꃁ⤽髁砃玞뵫䣦鶯풕⸊桮ꖣ煶⢂գ◉ﳮ徤喃교젆㴱驐闫频꫞鱜ؙ瑆⮘嶹涖ᦁ꣥ᨠ諷�攙귳귪癮愼ꢪআ扛콉沊짟䁦೭ၜ즱⨍꤮ⴔ饬꺔뙒逰ᡨ옸�縅錯㶎퉊�琀噕枝붚ဘ㿩ㄖ徍ଝ�骝〚숖垢ᣠ�씘ዥ㽙┛꾬㖭ꍬ诉酩攎伸脝먘⨀娙ⴥ仒∌啉귧⽼뗚矼⛤㻸麿ᵈˠ싋帮暚㪸쭀᭗Ǽ䦮橸뀅⩇蕒梄먜꒯벐⳥⓯黨�㧼ڟ쾁瀆懝镶젳�꩗폡呴䰨ꔹ䂮觮뽯ธ亰帡䇡אּ踼ꅁ♨疰솕䑉坧䡗䒂ꕱꎠ⦡摾螛멬끱뾼⸟ 傻o噊輹咔晹跆츪룬箷糌쭍ᅰ彨ᅬ☙쵢䢁ꍢ쾦럥䷮ᖀ⧧稞䶊뫵삡弓ᅧ拕䦼Ɦ끟峝츃䣓ᆭ淽⬊柸Ꮥ쌨廆慠㥴輅겞ꭟ㥎⇮൩玧㫂噂郦͒槟멡囼蛈ㅀታ屿硼㍾៴⯼㵲ڈ岚�Ɱ缺靹白혎뷟梖ꡣ뗄䥙⹅ㇰ쪪熤硤⦻퍱藵꓂封㡭㼯뱭雕伊ꃍ⠫☲鳝ꕶ⊿ⵠ뵝癶⛩롑螮ᤝ竷瑀댹믷㷘홒㐇Ŝꏘ涌ↂ浶꒺㔒㔊⣰䶉屮짊曾霫鯺㒕褼蝱䶼駲縑퉈鉤亴锵뷜ᷢ鶝뒟賽螁ᨔ毸�̖泄蔲�륁ܵ吟ԏ긫惨쿉듹҉韛׆飜軋曆㌪漴⡚⡘岊Ꞻ沴蘥鎎퍻㎟ꝼﲥ斱ꄎ፱뇥㵛쟐띘욾跧䥢ⶢッ鴥骺͎ᑺ辑끚鞺�鰗丨㰬ኜ阳�鶐큝䣄ꮘ칵鿩敉ffဍ龔˒쾉ڀ謏㊈ʭ跿瓗ࡸ◤ꟶ⫵ᝉ᭯跎엠慗乮怋냡鮓ࠌ癷囊ㆂ깨⻄쀉爦꺔关ﴛ�戄ꠦ웬㞚獏ᬅ骻䈋羳넥㍞㧻䯆⾈縟ᬌ㷶哇Ǻ͕ꚯ멗䋠蚁衛綟췂٫κ앩銱蘵咽惨媾캃ဂ툷욭䢮�瀮睅쵕蟝墿ೡ뗗缐昚ו杭ᠡ鉒䙬ꎳ魊鋒䟳�팸ⶇ庵뷸훧뺅ᡁਖ਼殙涐�Ⲟ䋗㜷皈䪀姎�॒㻱ⶮ픭⩴ḍ鷀깦句렐襪푋䥨⮈僕쏥�箅쫖ﭩ饽땆桝挡ꖍ쟉톆�Ṹ䚤⻛ꚡ潂㳢铛ᵦ瑽킁̇�䳹㛼ࣸ�ᩁ驎ꜭ⍗똜�춃쐛㢏Ṿ膱染畋༫㠘᷑♜ԓ鸙ᰪࡓ讧骩赼庡崽绎칔ﱞ䮉䍂溫者䩠襻깚춽齑林䒫மꜞ总摊佡⛨锊䨝咉惜黲狋턹쫔鶶嬢쭩蕟㑕꿧戮箥쀺ၖ뢕ﴼ㏤ꀚଡ븣援狀㜸齕㦙㾟뢾넜홺騧ⱆ學ᥲ뱠Ɥ৳혶ꘐ閶㾗䂱ㇿ궀魖㘪⇲ᯌ徼棑嫀퍁Ƥ㭒ꯎ�숧廪옚뱒ꉁ盥隨멀溜杌搩다틇椨ꨁ외范ᡊ殘ꡥ扳욶�ൎ䋨辈쫇㌕캉ﴄⰣ䌑兄긄뫴ཧ鏃⑃禍픟쬞⠛番棉㈹ᰫ幊ⷬ耡⾚됴�㌧ꪳ窄ﻶ㡪ۜ㵵惷䭘饱퍿Q쮏쏜穏ῆףּ쑦퇢겣哅荨봃ḛ鐍컁럹懕폫咻뫥鑹䙁䗟≌䧢벾琗ᆀྊ둕䣎瀋㫏逡銵�₅藸㻤ອ淚뚻ʤ悥孀ᅰం勒碳ᵍ圠䨨布�龓潞帩똥ሇ�տメ⼽䌇텊ꬂ왗맣ࣨ짻몧聠Ü娆㽌踾壕빣ꄖ頵㌄፣늓穱ꑀ厃ኤ鴣緔எၮ拲쏽ᕫᩍႎ䊻鸂᱉ꄮଡᲳ齣퉤髝⽍눑狮봪㽏剟뉧塶掺魁꪿뺦ʝ�᮳ၷ良ᕋ↙䱑㹤됊�Ӊ틢䑠忬餦畱蝳累嬕鶢쨢℄숆봩ﻊ꿒ਖ਼呅´�貫尵톪鎨釹㕩꯱닱ㄵ緽ヿ砥Ⓖꉟ鏲�㐟鄱厃嗪㓁벋성Ꭴ頳품䴴腚⼨彊큹윓쫑뱈ਗ਼ᗟ�褥羣ʧPᒫꂴ斪寁ῼ閭匹ଇ걡앩ퟓ舘第矤鲡鑔괢⇦蝓쾉皶脳馹뱊됒ϟ좶ヶꎚ戬葙軈ᾝ➮㒡䮯鋖旦醁竬鋐鵴蓔ﮃꮲ姱괄䒙ᇚ틥韯捹꤀�孄퉼﹡諄瑇畜됲᚛껄ꎋ룑�ы薙Ằ矌풏ﯼ쎐�ᖰ圏T╈╱芊߶痲生餭ꦯꎞ倜뻌彼숗沵퍛ῇ閜䄟䝴炆ꎢꔫ鮊硘ᶿ亘触䳹य़ᚮ폘艵秋襁奺輣卡W㓭쥥ꕢ檮彾籛癒ڥ姼㷕͖奐敊㙍✑뒌啅痈斒閞菞坐慝걔ଅ랾飶蚜┱项⤉ᵥ桄ᦘᑠ蒛랿퇩䪵⛬銖뭀闵쑚ꨛ똉땮鞻謺➎촥⛹콈잙稵ᡞ砫㤿믻䙕䡪攅힉⿈ॅ쨺Ꜳ㙜徬ꈛ鬜춊攁ƿ㴏�햕�祁�螽薪锐뜪宸嫗揇枌偡�⯒퐬軗湚仹ﴸ譐巋嚀ᾈ拽着̩�倍䍈䝍犟㇉嬕莺꩓Ⰵ�嫮跣糑㾬ྑ嚜䬰셃燷鄘錄瘷ꦄⳠ閧㸥땪䆵皏휬頯믋ଆ簭舺땨뻮ꐫԖ놹̂ၷ櫛〠ꥩ㲐獯癔に裞輭�妅᠀鉝靽╗Ç㼆睾滳빇듽ꃶဋ틑ઢﮀ꠸ዷ꧃鎬괳ꟼ戮徦㸈䌡잳휠혐쳅翂궥볢埒徝鎏莦�嗻匉䛿䣄�渶殓溉녀ᩘர첾�觕晍츒௴䑋ẛ좰⬖旀톴᩵曨溛�뎳휹㋱糳셨䆚歗螠縪볟㠞猳ꗬ�チ嫵汥劖鼴�遆剃a虄䄼뜓蓐⹊⺝闠屽⤦稷籄笮쵦䫚퐨⠲괐홆愍먢큒鷽溹靛ଅ䣉鎲뮱Ș�꿓롨䑉䗋樉侍�䑒嚇ࢅ丷捜��ሇ�킕푴ʨ樍ꐻ龹餼勉䢜⛔웲걐겆灠厐㝙귱뻣丄年窨⠎鬜᫁䅉ﳹᨨⅬ駊鏨䲷ﲦ宥墦谭Ⳝ쉊먣풤觛⣑ɾಌ䪢ሶ⯏擯ꬾ酉㙸윻ﳣ矜뮠㠓킯絅炯丼힑䷀쐬㴐㫭ᷯ겢䑃뽹燎鉀ヘ価탻䭢ꕌ້忼ㅞ穎웎惜㹬囼溢윞瘶ƍ䲳᷊龙뼊敫칍圤祉慸摂≺쥗ꇯ롂烣㛂藨�◌ᬝ䌒�卐ꚿ㍃ṉ쵥轰晼檟骵蘪鑧䒶簕蒯䚲㋐႙꺸戄顡ꃏꀢ손㖔卑箍怚낪﴿灜䔌덪죕純䘟娥䩢﵂䈝�㔹凐昫깲ꁑ갍왫駋⡘ﱹ䉤礪螝娉냰흷扷☛싥扗ῃ䋨궔ꨱᄑ柳䵡ᩃ嘱䀜彙췌汥콾묚ེ눢닖↧ᵢ笈填Ꝍम関黷䕟桉Ęᓟ븗蚮졮㤘도㱶숌遤굫ꖭ㴇뛵费戤㌀ꌉ袾㱤봁ઉ全ເன㦆ⱘ刘䔿樎ᗿ䷭䀽ꁱᕞ⺉轔숿璠«玑㘌袸ಹמּ鼕鲌輪K㭈匎ᒡ☽歋臃툨ᴪ뽹�ᄋ除郫ὴ㋑怿뚘쪈辛展싿䛷뾎ᡂ펙䨍䟅앹ꠃ蚀홞䟭륳ࡓ锁辂킷眈�羚ね☑┃叾Γ昸闿哏䪺諿㰍ࣾ○䤱ඍ컵䘍㸬仝軐दꇐၸᬈ햵꺳㪦꡴㳩ṷꅅㅊ㜸頨梮藃︉嚱욆箘컝릭Ṛ橓웮辉躑룴ᬏ颏閽嫜홷튪ባꭠㅗ㎱r᭢㌃垗ꎙڮ퐑澑昐뢫廙朢Ⱁᜌ⼤崐�鋂ࢾ㵝´ﳔ髚ܱ娢玦ᾫ됒뉦ⴇᖞ�⮶橦鐜㏔ピ뚖嫑᫊蓔ꮾᯬ⯩摕듶튆轉즆车쐇哲鄌겷స䝝륁쓚₩幱肇䵊铒솠콊▂Ῠ荧㵦젉턈·ǐ▮鏗梠쭴ﱏ鯺䘢醀ﶼ庘䁊鼌ᑠ鳊碥ﷵ午珰尸盡㹮ﳇ뇌礊䐎狺詈닁复ᆍቿ렗䚪뉚��㘚柭൹䎅딮㚒備慂ﯳꑼ謬윍펺⦌郬눱뫋鿱ţᎎᯉ✋㟰瑈抉앞贎퀧�褙牮蕂監鵣靊ᦆ霎薳蠸ᔴﯿ㪫ㄫ횓�슣藋訢︅錺㿩ⳏ먆�꼚Ӿ㓵㌱揝�饀殷㵯嘦Ꝭ춒쥉먤뫝�雸쉞㼜𢡄ᩒ㸊꣤戦锢曍籂瀶葶데롴ᛓﯟ룙ھ넼읥濸峈资ை꼒輴�㤭ṤĽ쉾壾曔鬃霚䥴䒍諶타ꗺٿ꺅췈勅༐䲟䢞憵ᩩ話⢗⟒Ꜹ῀ⷾꞹ↤ൠ᪽츌ꛏ癑읃Ⅳൠ꿐䎔냋ࡆ㎇䓵严珰䍈ꏄʇ㏊麂쒭鲍읱렻扰ג鿠�寪ધếߧ�揨互ⓦ斾ᶄ⭡䞅峬䤔ꤙ亗隀毕宜딹ሟ펼먵�鴒헗fi톀薸㵇帱㤏ᐟ䂈Ꭿ矫紦㫅魐뇽⾎账竑쳪澼턳캸ꋏ놞僼㎀ꕉ㷑釯湳﹒㳫麹옳乞幡ꭀ밝ֽ࠼꿥꫁Ƚ⩢㕏뢅�堖⭱ꋶ鵹�ﶦ䮟湦첿නᗬⶫ嗦ᘤ⨶灔㵹⭂蒎࿑掫䌞쮟楪鼧䥇望鄞嗫葦菡驯嚇ඨ騠踆㫤ꌓ㬆櫥伱渟년뼐聗칕吜諚骼䗛�ࠒ䃥㠁䚁戇ⵓ밶㕊㋕餽㹭䇣짜뷯홃뒠蠿棋姞⮷碽謿Ε㍭ᒚඏ㴄罩⃧죠熣温Ᵽ곛緾孺ސஅ꒳䰽卽䅬혙�診퉛ظ丼㧑표庞옒蔃�닌앻ẇ犹牔♉��ǩ橊★㶷綎玚㦚穷��㴚ꂷ㕒�禐⮜律ꔩ뮕烠㣀᭘飶ꄀ괌곝ု̐⊙ߤᆝᑐ买뙢縬㨭暩ূ⅄㜚篂樷ꓘ൙�츶ྩꤾ鱣䪑猺뜥㼔緧砥朊㕅퓻芧뻶욢쒚Ỵெ窨轷ㇰ굶Ꮡ踝㒩愑⻱贼8윯ࠣ샽掮漵寬湋갔騋飊⭦擺饐柮졙ꪋ䪻ă뇐倨ꟙ笄鸻ࢭ缋꧲흉鏢য়缆栛鶣�ඔ걽痫珉ᩃ榲�䱞㉈됮嚈᭵墵埞폂薋笯뉙㶏鞲뢜鶨ꢾ䡋넥┫囧ꄾ繱詿⽱�䚛ꗄ⑽㙫踶鉗ꍲ頻䐑㚕븆顔癪퇸颍洖爤猼ꆇꃱ㱷⨵繀뎬௭朅襴菼욅ہ�ߦϢ籹୰虎銀鏚칉굹ꂺ詖怒荧Օ≃�嵱햠혟艹鋃玕癫䍋淼㎕풡�ԅ⋢㲡质쓞ࣔ┚魜ྎ勣儤䙣㥇�훂ᛯ뚚㧢驀�᮵᱔놵Ꟗ�삂ﵜ餜ᠶ㦉퉡䔀場⛋ꉥ꼱⩞ꤿꞇ튙フ楟릐讂Ự쾯谪˂쐷第鑸Ձ缏失�悦㮫㼏ᛎ譪嵰ჵ흤鏈驌㼓ﳖ㑎휋ꈾ滑떎䥤�䊫㮤ᰀ豼ꂋ䍦諷bꇤ謻掽ᬍ뽨瀞暵꺳☰␥鵣㯙땯瞻쪄氐뺳纞愍ʛᮍʿ蝙댇娽잲䧠ђ✽拏ㄡ럀钤햓墅鮂ⴇ䩘拁殦귘㶘쉄䇭䘩꿩Ȣ퇢䐇༆侽瀼盉ꭘ⌷ᠿ熌ꚛ䨘왪嵼橾눝ĩĕ鋩䷹晫蓎ꮦ㡯鐾➞ﰞ냏飅젎퓂ꇽ犱⢞ਈ亵ᆭ愀잷絯胚덈諍꺖뎜럞ⶎ瓑嘯줍䚂翜戓翇㜪浼裦-ᕶ뛨行並娂ꥒ뚄贯ꐢĕẈ蛹첾岾�݇ᣄ넺䡑隼嵴笏싌༦瓙ὔሽ꾞疬륰㕑�麥�揓팄갞륨٦櫟큧Ე훼췚煫㎀ﲲ芜均酧೯흇覛粋︌깤잡⸲䔁蕷醍ꨩᬒ岡轡꘤踲픋랸ꑃ녓骨㛑፝ⵈ柢潅ᇄ낗쩽贪糜୶剘텆麦遁䃲誦횥志蓟�밐䉣檳꒴洪⍑�盢ᣵ蔁巬蔍췄ꛅ쓿若湥쀍䵘ﻠ鄌雃缬㇢輫颥뀕쎾ᴗ苿ᅧຍ腑춆뱊뵎ᝀ鞙킒�퇩䒔㠖걛땂诬榮꿬㤷覊嵋㦡醃仲䩔嵅嘂Ⴔ♀▙뙴蕒픁ᕭ昑彛딾�ힿ覑葧梒蒍쭅棷芿籧⥛鄫闉⽘칟쉽壪㉏Ń绢뫯堝⣍툍鴺譼舤옶릮꼚�恀ꢽ旽삾욅�䂮詏⟏䜀턺덧鯁燣廮묲扮佁㘦濿㯨�⇻�筈ଏ½㝖③끀農ꕪ鳽睊뵗꿀᰾恤ѹ≪羌࿓㲇痘¡㎇髮뇄ἴ斎룺蕆ᄋ攖傷�騢忰畞Ꙟ�塴⯄倽ﲪꒇ廓ทয়㘿줺죉਼Ѿ⥍ಇ�顸坵ㄢ氼᷎⾖餏렽ⶭ黳糇異鄮떔钱钤쵕菁櫪뉖'䦫롎꺔皤濉痠컐퓻펠틧Ẕ㣡聿톘᷆㸾檛�ᛩㆄ�ꖼꫧ둮�ꋽꬽ♌卋兽⯉�⼀쇰ުLᦋ닳惶췢膢㚚䶊ണ빘)맂㼞︪㗣ㅗ硞畮勞�䋖酸픫惁솸ﯲ�料Γ텙각诶�τ킸ꭂᶆౙ鷬緎�ﱯ섍퓗㿨䒌틆岯�㍎Ꝥ쵾ᾂ䒥Ë꼱᪲ꍹ橢휬ഴ墲鎗쑌鴆謴ꠚﰿ꧱ቈ烖躋迤⇹鐝抻붇�墈顺먍ꈾ鵢鞑㐻鼮Ỹ幼辱鎖Ⓛᖄ㣵❏닒묏굍鋿꺐꧀叻逈閕㛭䎜쮊�櫺凘韡椑ⴱ됣弇瞦왯ཋ芚ᄃ暣牳횞㵺ꢮ垡믆⽯敻쇫쎨醪✨鉅薂匝褤ਮ༷龷᠀䂲烛뗇墕♲钹ᦌ脫商奸軗㧝꼳葯꾏㪐ᒆ쿾ﻭ㚲ꙝ瘣ꍫ麁凷䰤帠ྡྷ鉚쏱캘㰺욶손蟌뜆㸈ᤴ嵧灈ᶌᱹ珨ᕀ귔윥及ඔ⣝⌠嶾퉠闏⭸꜋虮엠䛏�㓗ꆝ㹾䯨⅕ࠩ㙼嗍Ꞝ䌖͝哻귃砭䑂젒凛쇉阣㗱켏喝�ጫヱ胈䃢ᏹޘ�쇻킱Ḓ�䥗먬獋樘ʍ谹웈ꏿ鮀풃౮䎠맛ᤰ隮䭍㕬쬝ʲᇇ였㍍ክ餏풠Ⓦ걭簡顪繰腁띚،禉�袹疢够挹횕ຖ퓦纼蓰葔쩐殨嫓둁˨㫗隢ኚ谙濱ਥ鬱㗤ፓ쪻䒨㛒椎垥錤抪櫱귄㇈抳网ቝറ殙쨋ⵆ㧇䏇街䐹㎩㪚늣轎懘㿺쁨嫞횧릚줱姕�ᖉ峲猋煍蹩ꂥꭏ荿㘇騌ᦃ鶉⎈爽퍲䯸直鞭Ⓡ⤣롽㭸乨썲㤎ꮜ傷藞䧨汱寍ƙ䗦툋⥏别櫗䏦ꑲ갺䅇똉䪛鰡䘾鵯촻幔䱮欶陛ຝ⾸檜㐳研턛琡멘ޘⲃ힜ふ匢줟葀댸폫䚏㡳뛭憝쫯ᮍ⬺ﴯ笰ڛ콐㼎곴㻦医ꅿ仈涠賲埻柳ﶸ�縧㠎᳕䆩親誱릤妍鼅ბ퀏叉穰堮雬觿ꚴ䶝焤颭ﳟ䎔鶜嫲锬턒㈒뺝⤣�廤ꑵ穈廘郜⡺궳ၘ粗찤梁�믿圂셲璖䬢Ợ厎곑쾞�쁑�癤䴖닆졖�晨劀⩩騚⾴猀В⒫㈣룟ꥇ趧�̆楰泣ꁶ䅊珇枊䔓噓쒠ﬦ槄帲Ȝ攚霪羥⨐㫳�뷗찝㪓⨼확䰊Ѹ㷿ꦭ潠徘됕睬џ퍐䝷⮪娠땱�䄋诔䴪ᰱᖔ聧樑刚㫡펰횟민嚲ᓖ䰶떩য়ꝢḈ愈⾅ᰥ寥諢擱㷤墴ӧ圻䡋㊐吰䡡ꛣꃘ쁾・藫퉜햽胰姧䚃捏㼻춿虣ᜣ䂽㆐⃐ℶƖ镴뻶ꉌၪ腃굒仱堖痱ꢜײַコ䒨㾦緞놭䧕⊠孔䆄矛㫆핫၂蔿ꥡ늮羴Ԕន망ᙓ䭞㱕铧䬄酙㐞胕颈᧴⠵劫⏯턪耷㰮Ԩ☽㻗뜚�罱釶簥苑뱝᭺㐭藍䗺ⅶ〙鱀䳬痞㸨葵됭㯏婊봩媛⑮ᬽ쀩Ⰸ뻄翸䁯ﺞᨶ㆒�鰐칬粃츇Մᚋ嵫온䇨嵺눲韛䕚靶ǻ槳优斋㔏밨儘涪ﴗ
The text encrypted here is taken from a popular website. Good luck guessing which one :). And yes, it decrypts back perfectly!
OK, so how come this is rarely used if it so simple to implement and yet pretty much impossible to break? The problem is sharing the one-time pad — especially for long messages. The security of the one-time pad is precisely its main weakness — it’s way too unwieldy. A 1000-KB message requires a one-time 1000-KB key to be perfectly unbreakable. How do you share a huge key like this without malicious parties intercepting it?
As I had mentioned, spies in the Cold War era actually used this method, but they had much shorter messages to transmit, so their keys needed to be much shorter too. How did they communicate keys? Well, one way to do this was to utilize some publicly available source of randomness — like classified ads.
In one of my favorite movies, The Beautiful Mind, John Nash goes insane trying to analyze and spot patterns in classified ads of different newspapers across the country. Below is a listing of classifieds taken from a 1970s California paper.
How could we use something like this? Well, we could have a agree on a channel — a predefined paper, day and page, even a different column for each day of the week. Then we take classifieds that are posted, and create our random one time pad by converting each word to a number with some simple scheme (see below). What makes classifieds ads superior to generic text (like a poem or the Bible) is the addition of random bits like prices, abbreviations, phone numbers — all interspersed throughout variable length chunks of text. There are some patterns — but they are nearly impossible to detect in one-time pad scheme of encryption where every bit is only used once.
function getWordValue(word) {
var val = 0;
for(var i=0; i< word.length; i++)
val += Math.pow (word.codePointAt (i), i);
return val % UTF_LIMIT;
}
function generateOneTimePad2(wordArr) {
var randArray = wordArr.map(item => getWordValue(item));
var reducer = (accumulator, currentValue) => accumulator + String.fromCodePoint(currentValue);
return randArray.reduce(reducer, "");
}var keyPart1 = generateOneTimePad2(["Factory", "Meyers", "Manx", "top", "curtain", "Gates", "extras", "Sacrifice", "$1500", "882-2732"]);console.log(keyPart1);>> ♬酩跀ㅰ྿붜ḴᏫ뭽江var keyPart2 = generateOneTimePad2(["'69", "Dune", "Buggy", "green", "metal", "flake", "street", "legal", "best", "offer", "over", "$1200", "805-526-9193"]);console.log(keyPart2);>> ೨\ue866뺖\uf3c2ᭁ늸忶ဢէᇙ쎮멈镾var keyPart3 = generateOneTimePad2(["Chev.", "'67", "Camaro", "SS", "350", "780", "dual", "feed", "Holley", "400", "h.p.", "Z28", "heads", "&", "cam.", "Hedman", "heddlers", "Solids", "Edelbrock", "high", "rise", "manifolds", "American", "mags"]); console.log(keyPart3)>> 資ఈ낭Tशह帊檎ऱ굨㝴\u0001ꬄ\udbda빆⸅뚶召\uecbf泀䥽廕
var secretMessage = "Meet corner of Smith 6pm. Package with me";// length of secret message is 41 chars, length of combined 3 key parts is 48 , we are good to go!var encr = oneTimePadEncrypt(secretMessage, keypart1+keypart2+keypart3);console.log(encr);>> ☡鄌趥ྟ뷿ṛ᎙묓氺ಚ뻹᭡닫徛။ԓᆱ쎎멾锎貪ద낍�ॗग़幡櫯ﶳ॔굈ఄ㜝u�븫
var decr = oneTimePadEncrypt(encr, keypart1+keypart2+keypart3);console.log(decr);>> Meet corner of Smith 6pm. Package with me
So, there you have it — a secret channel to function as a shared source of randomness, and a one-time pad encryption mechanism. Now, go out and do some spying already!