এই পর্বে যা শিখবেন
- Plaintext, ciphertext, key, encryption আর decryption-কে ফাংশনের ভাষায় লেখা, আর Kerckhoffs-এর নীতির অর্থ
- Shift আর affine cipher হাতে চালানো, affine key-এর বৈধতার শর্ত প্রমাণ করা, আর inverse দিয়ে decryption
- নিজের বানানো ciphertext-এ brute force আর frequency analysis চালিয়ে key বের করা
- Vigenère cipher, আর Kasiski ও index of coincidence দিয়ে তার key-এর দৈর্ঘ্য বের করার ধারণা
- One-time pad-এর perfect secrecy-র প্রমাণ, আর key পুনর্ব্যবহারে কেন সব ভেঙে পড়ে
১ · গোপন বার্তার গণিত — cryptography-র শব্দভান্ডার
মডিউল ৪-এ আমরা সংখ্যাতত্ত্বের যন্ত্রপাতি বানিয়েছি: gcd আর extended Euclid (পর্ব ২১), modular arithmetic (পর্ব ২২), modular inverse (পর্ব ২৩), আর Euler-এর φ (পর্ব ২৪). মডিউল ৫-এ সেই যন্ত্রপাতি একটি পুরোনো প্রশ্নে লাগানো হবে: একটি বার্তা এমনভাবে পাঠানো, যাতে পথে কেউ পড়তে পারলেও বুঝতে না পারে। এই বিদ্যার নাম cryptography.
কাল্পনিক অনলাইন দোকান বাজারঘরের অফিস থেকে গুদামে প্রতিদিন একটি অর্ডারের তালিকা যায়, যেমন “SEND TEN SACKS OF RICE TO SYLHET”. ধরুন তালিকাটি খোলা পথে যায়, আর অফিস চায় পথের কৌতূহলী কেউ যেন সেটি পড়তে না পারে। এর জন্য অফিস আর গুদাম আগে থেকে একটি গোপন সংখ্যা ঠিক করে রাখে। এই পর্বে আমরা দেখব পুরোনো দিনের পদ্ধতিগুলো কীভাবে কাজ করত, আর গণিত দিয়েই দেখব কেন সেগুলো ভেঙে যায়।
একটি cipher হলো পাঁচটি জিনিস: বার্তার সেট 𝓜, ciphertext-এর সেট 𝓒, key-এর সেট 𝓚, আর প্রতিটি k ∈ 𝓚-এর জন্য দুটি ফাংশন Ek : 𝓜 → 𝓒 আর Dk : 𝓒 → 𝓜, যাতে প্রতিটি m ∈ 𝓜-এ
Dk(Ek(m)) = m
m হলো plaintext (মূল পড়ার মতো বার্তা), c = Ek(m) হলো ciphertext, Ek হলো encryption আর Dk হলো decryption. 𝓚-কে বলে key space, আর গোপন মান k হলো key.
শর্ত Dk ∘ Ek = id মানে Ek একটি one-to-one ফাংশন: দুটি আলাদা বার্তা একই ciphertext দিলে প্রাপক বুঝতেন না কোনটি পাঠানো হয়েছিল। এই পর্বের সব cipher-এ 𝓜 = 𝓒 (একই অক্ষরের ধারা), আর তখন সসীম সেটে one-to-one মানেই onto, অর্থাৎ Ek একটি bijection আর Dk = Ek−1. ফাংশনের এই ভাষা পর্ব ১৬-এ এসেছে; এখানে আমরা সেটিই প্রয়োগ করব। Cryptography-তে একই key দিয়ে তালা দেওয়া আর খোলা হলে তাকে symmetric encryption বলে; এই পর্বের সব cipher এই ধরনের।
Kerckhoffs-এর নীতি: গোপন থাকবে শুধু key
ডাচ বংশোদ্ভূত ভাষাবিদ Auguste Kerckhoffs ১৮৮৩ সালে ফরাসি সামরিক পত্রিকায় “La cryptographie militaire” প্রবন্ধে সামরিক cipher-এর জন্য ছয়টি নীতি দেন বলে উল্লেখ করা হয়। তার দ্বিতীয়টি আজ Kerckhoffs-এর নীতি নামে পরিচিত: পদ্ধতিটি শত্রুর হাতে পড়লেও কোনো অসুবিধা হওয়া চলবে না। অর্থাৎ ধরে নিতে হবে প্রতিপক্ষ E আর D-এর সূত্র জানে; শুধু k জানে না। Claude Shannon ১৯৪৯ সালে একই কথা ছোট করে বলেন: শত্রু পদ্ধতিটি জানে।
কেন এই নীতি? পদ্ধতি একদিন ফাঁস হবেই, আর key বদলানো সহজ, পদ্ধতি বদলানো কঠিন (ভাবনার প্রশ্ন ১ দেখুন)। গাণিতিক সুবিধাও আছে: পদ্ধতি প্রকাশ্য হলে নিরাপত্তার প্রশ্ন দাঁড়ায় একটি পরিষ্কার গণিতের প্রশ্নে, “key না জেনে c থেকে m বের করা কত কঠিন?” এই পর্বের প্রতিটি আক্রমণ এই নীতি মেনে চলে: আক্রমণকারী সূত্র জানেন, key জানেন না।
নিরাপত্তার কথা আগে: এই পর্বের সব cipher (shift, affine, Vigenère, ছোট উদাহরণের one-time pad) শুধু গণিত বোঝার জন্য। এগুলো দিয়ে কোনো আসল তথ্য, পাসওয়ার্ড বা গ্রাহকের ডেটা রক্ষা করা যাবে না; নিচে দেখবেন কয়েক সেকেন্ডে ভাঙা যায়। বাস্তবে যাচাই করা library, authenticated encryption আর বড় key লাগে, আর নিজে cipher বানানো চলে না। হাতে-কলমে নিয়মগুলো Cybersecurity কোর্সের পর্ব ০৭ (ভিত্তি), পর্ব ০৮ (AES আর authenticated encryption) আর পর্ব ১৪ (সাধারণ ভুল)-এ আছে। এখানে সব আক্রমণ শুধু আমাদের নিজের বানানো ছোট উদাহরণে।
২ · অক্ষর থেকে সংখ্যা — shift বা Caesar cipher
গণিত চালাতে অক্ষরকে সংখ্যা বানাতে হয়: A = 0, B = 1, …, Z = 25. এখন প্রতিটি অক্ষর ℤ26-এর একটি সদস্য, অর্থাৎ 0 থেকে 25-এর একটি ভাগশেষ, আর যোগ-গুণ চলে mod 26-এ, ঠিক ঘড়ির কাঁটার মতো: Z-এর পরে আবার A.
𝓚 = ℤ26. key k-এর জন্য প্রতিটি অক্ষরে আলাদাভাবে
Ek(x) = (x + k) mod 26, Dk(y) = (y − k) mod 26
k = 3 হলে একে ঐতিহাসিকভাবে Caesar cipher বলা হয়।
রোমান ইতিহাসবিদ Suetonius লিখেছেন, Julius Caesar ব্যক্তিগত চিঠিতে প্রতিটি অক্ষরের জায়গায় বর্ণমালার তিন ঘর পরের অক্ষর বসাতেন; নামটি সেখান থেকে। নিচের চাকাটি ঘুরিয়ে বসানো দুটি বর্ণমালার মতো: বাইরের অক্ষর পড়ে ঠিক ভেতরের অক্ষর লিখলেই encryption.
Shift cipher-এর চাকা, k = 3
ভেতরের বর্ণমালা বাইরেরটির চেয়ে তিন ঘর ঘোরানো। চাকা অন্য পরিমাণ ঘোরালে অন্য key, মোট ২৬টি অবস্থান।
(ক) প্রতিটি k-এ Ek হলো ℤ26-এর একটি bijection, আর Ek−1 = Dk = E−k. (খ) Ej ∘ Ek = Ej+k.
প্রমাণ:
- Dk(Ek(x)) ≡ (x + k) − k = x আর Ek(Dk(y)) ≡ (y − k) + k = y (mod 26). — congruence-এ যোগ-বিয়োগ, পর্ব ২২
- দুই দিক থেকেই inverse আছে, তাই Ek একটি bijection আর Dk তার inverse; আর y − k ≡ y + (−k), তাই Dk = E−k. — inverse ফাংশনের সংজ্ঞা, পর্ব ১৬
- (খ) Ej(Ek(x)) ≡ (x + k) + j = x + (j + k). ∎ — যোগের সংযোগ নিয়ম
অংশ (খ)-এর একটি ব্যবহারিক মানে আছে: দুবার shift করলে নিরাপত্তা বাড়ে না, কারণ ফল আবার একটি shift. 3 আর তারপর 5 সরানো মানে একবারে 8 সরানো। গণিতের ভাষায় ২৬টি shift ফাংশন composition-এর অধীনে একটি group গঠন করে; group-এর পূর্ণ তত্ত্ব আসবে পর্ব ৭০ “Group — symmetry, cyclic group আর Lagrange-এর উপপাদ্য”-এ।
কেন এখানে বাংলা অক্ষর নয়
বাজারঘরের বার্তা বাংলায় কেন নয়? কারণ তিনটি। প্রথমত, ইংরেজি বর্ণমালায় ঠিক ২৬টি অক্ষর, আর প্রতিটি অক্ষর একটি সংখ্যা; mod 26-এর হিসাব পরিষ্কার। বাংলা লেখা কম্পিউটারে Unicode-এর একটি ১২৮ ঘরের ব্লকে থাকে (সব ঘর ব্যবহৃত নয়), আর পর্দায় একটি “অক্ষর” প্রায়ই কয়েকটি code point জুড়ে তৈরি: যেমন “ক্ষ” আসলে ক, হসন্ত আর ষ, তিনটি আলাদা মান। দ্বিতীয়ত, প্রতিটি code point আলাদাভাবে সরালে কারচিহ্ন (া, ি, ে) শব্দের শুরুতে বা হসন্ত ভুল জায়গায় পড়তে পারে, ফলে ciphertext পর্দায় ঠিকমতো দেখানোই যায় না। তৃতীয়ত, frequency analysis-এর জন্য একটি বড় বাংলা লেখার সংগ্রহ থেকে মাপা অক্ষর-ঘনত্বের টেবিল লাগে; সেই সংখ্যা আমরা বানিয়ে বলতে চাই না। তাই এই পর্বের সব উদাহরণ ইংরেজি বড় হাতের অক্ষরে, আর ব্যাখ্যা বাংলায়। গণিত অবশ্য একই: n-টি চিহ্নের বর্ণমালায় সব হিসাব mod n.
৩ · Affine cipher — x ↦ ax + b, আর কখন তা উল্টানো যায়
Shift cipher-এ শুধু যোগ। গুণও যোগ করলে পাওয়া যায় affine cipher: প্রতিটি অক্ষর x যায় ax + b-তে। কিন্তু এখানে একটি ফাঁদ আছে। a = 2 নিলে A (0) যায় b-তে, আর N (13) যায় 26 + b ≡ b-তে: দুটি আলাদা অক্ষর একই ciphertext অক্ষর দেয়। ciphertext-এ সেই অক্ষর দেখে প্রাপক জানবেন না আসলে A ছিল না N. তাহলে কোন a চলে?
key হলো জোড়া (a, b), যেখানে a, b ∈ ℤ26 আর gcd(a, 26) = 1. Encryption আর decryption:
E(x) = (ax + b) mod 26, D(y) = a−1(y − b) mod 26
এখানে a−1 হলো a-এর modular inverse mod 26, অর্থাৎ যে সংখ্যার সঙ্গে a গুণ করলে ≡ 1 (mod 26) হয়।
a, b ∈ ℤ26 হলে ফাংশন f(x) = (ax + b) mod 26 একটি bijection ℤ26 → ℤ26 ⇔ gcd(a, 26) = 1. তখন f−1(y) = a−1(y − b) mod 26.
প্রমাণ:
- (⇐) gcd(a, 26) = 1 হলে Bézout-এর সূত্রে sa + 26v = 1 এমন পূর্ণসংখ্যা s, v আছে, তাই s ≡ a−1 (mod 26). — Bézout আর inverse, পর্ব ২১ ও ২৩
- g(y) = a−1(y − b) ধরুন। g(f(x)) ≡ a−1(ax + b − b) = a−1a x ≡ x, আর f(g(y)) ≡ a · a−1(y − b) + b ≡ y. — বসিয়ে হিসাব
- দুই দিকের inverse আছে, তাই f একটি bijection আর g = f−1. — পর্ব ১৬
- (⇒) উল্টো ধরুন d = gcd(a, 26) > 1. x0 = 26/d নিন; তাহলে 0 < x0 < 26, অর্থাৎ x0 ≠ 0 (mod 26). — d ≥ 2
- a x0 = (a/d) · 26 ≡ 0, তাই f(x0) ≡ b ≡ f(0). দুটি ভিন্ন ইনপুট একই আউটপুট দিল, f one-to-one নয়, তাই bijection নয়। ∎ — contrapositive, পর্ব ৮
উপরের a = 2-এর উদাহরণ ঠিক ধাপ ৪–৫: d = 2, x0 = 13 = N. a = 13 হলে আরও খারাপ: d = 13, x0 = 2, আর 13x mod 26 শুধু দুটি মান নেয়, 0 আর 13. পুরো বর্ণমালা ভেঙে দুটি অক্ষরে নেমে আসে।
কয়টি key আছে?
26 = 2 · 13, তাই gcd(a, 26) = 1 মানে a বিজোড় আর a ≠ 13. এমন a-এর সংখ্যা Euler-এর totient φ(26) = 26 · (1 − 1/2)(1 − 1/13) = 12: a ∈ {1, 3, 5, 7, 9, 11, 15, 17, 19, 21, 23, 25}. b যেকোনো 26টি মান নিতে পারে, আর ভিন্ন (a, b) ভিন্ন ফাংশন দেয় (x = 0 আর x = 1 বসালে b আর a + b আলাদা হয়ে যায়)। গুণের নিয়মে মোট key
|𝓚| = φ(26) · 26 = 12 · 26 = 312
এর মধ্যে (1, 0) একটি অকেজো key, কারণ সেটি কিছুই বদলায় না; আর (1, b) জোড়াগুলো ঠিক shift cipher. সাধারণভাবে n চিহ্নের বর্ণমালায় affine key-এর সংখ্যা n · φ(n).
এবার হাতে চালানো। বাজারঘরের প্রিয় শব্দ BAZAR-কে key (a, b) = (5, 8) দিয়ে encrypt করা হবে। gcd(5, 26) = 1, তাই key বৈধ।
1a = 5, b = 8, gcd(5, 26) = 12B = 1 → 5·1 + 8 = 13 → N3A = 0 → 5·0 + 8 = 8 → I4Z = 25 → 133 ≡ 3 → D5A = 0 → 8 → I (আবার)6R = 17 → 93 ≡ 15 → P
| অক্ষর x | |
| 5x + 8 | |
| mod 26 | |
| ciphertext |
- Key যাচাই। a = 5, b = 8. gcd(5, 26) = 1, তাই উপপাদ্য ৩.২-এ E(x) = 5x + 8 একটি bijection, decryption সম্ভব। অক্ষরগুলোর সংখ্যা: B = 1, A = 0, Z = 25, R = 17.
- B. 5 · 1 + 8 = 13, আর 13 < 26, তাই ভাগশেষ 13 = N.
- A. 5 · 0 + 8 = 8 = I. লক্ষ করুন, A সবসময় সরাসরি b-তে যায়।
- Z. 5 · 25 + 8 = 133 = 5 · 26 + 3, তাই 133 ≡ 3 (mod 26) = D.
- আবার A. আবার I. একই plaintext অক্ষর সবসময় একই ciphertext অক্ষর দেয়; এই দুর্বলতাই পরে frequency analysis কাজে লাগাবে।
- R. 5 · 17 + 8 = 93 = 3 · 26 + 15, তাই 15 = P. ফল: BAZAR → NIDIP.
এবার গুদামের দিক। প্রাপকের কাছে আছে NIDIP আর key (5, 8). তাঁর প্রথম কাজ 5−1 mod 26 বের করা, extended Euclid দিয়ে (পর্ব ২১).
126 = 5·5 + 1 ⇒ 1 = 26 − 5·525⁻¹ ≡ −5 ≡ 213N: 21(13 − 8) = 105 ≡ 1 → B4I: 21(8 − 8) = 0 → A5D: 21(3 − 8) = −105 ≡ 25 → Z6P: 21(15 − 8) = 147 ≡ 17 → R
| y | |
| y − 8 | |
| 21(y − 8) mod 26 | |
| plaintext |
- Inverse বের করা। 26 = 5 · 5 + 1, তাই 1 = 26 − 5 · 5, অর্থাৎ 5 · (−5) ≡ 1 (mod 26). তাই 5−1 ≡ −5 ≡ 21. যাচাই: 5 · 21 = 105 = 4 · 26 + 1 ✓.
- N. y = 13, y − b = 5, আর 21 · 5 = 105 ≡ 1 = B.
- I (দুবার)। y = 8 = b, তাই y − b = 0 আর ফল 0 = A, দুই জায়গাতেই।
- D. y − b = 3 − 8 = −5, আর 21 · (−5) = −105. ঋণাত্মক ভাগশেষকে 26-এর গুণিতক যোগ করে ঠিক করুন: −105 + 130 = 25 = Z.
- P. 21 · (15 − 8) = 147 = 5 · 26 + 17, তাই 17 = R. ফেরত এলো BAZAR, যেমন উপপাদ্য ৩.২ নিশ্চিত করেছিল।
নিচের ব্লক এই দুই stepper মেলায়, পুরো অর্ডার-বার্তাটি encrypt আর decrypt করে, বৈধ a-গুলো আর তাদের inverse ছাপে, আর অবৈধ a-তে কতগুলো অক্ষর মিলে যায় দেখায়। Python-এর pow(a, -1, 26) সরাসরি modular inverse দেয় (Python 3.8 থেকে); inverse না থাকলে এটি error দেয়। প্রথমবার চালালে ব্রাউজারে Python (Pyodide) নামতে কয়েক সেকেন্ড লাগে।
from math import gcd
A = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
def check(a): # a key is valid iff gcd(a, 26) = 1
if gcd(a, 26) != 1:
raise ValueError(f"a = {a}: gcd(a, 26) = {gcd(a, 26)}, not a valid key")
def encrypt(msg, a, b): # x -> (a x + b) mod 26
check(a)
return "".join(A[(a * A.index(ch) + b) % 26] if ch in A else ch for ch in msg)
def decrypt(ct, a, b): # y -> a^(-1) (y - b) mod 26
check(a)
ai = pow(a, -1, 26) # modular inverse (Python 3.8+)
return "".join(A[ai * (A.index(ch) - b) % 26] if ch in A else ch for ch in ct)
c = encrypt("BAZAR", 5, 8)
print("BAZAR ->", c, "->", decrypt(c, 5, 8), "| 5^-1 mod 26 =", pow(5, -1, 26))
order = "SEND TEN SACKS OF RICE TO SYLHET"
c = encrypt(order, 5, 8)
print(c, "->", decrypt(c, 5, 8))
valid = [a for a in range(26) if gcd(a, 26) == 1]
print("valid a:", valid)
print("number of keys:", len(valid), "x 26 =", len(valid) * 26)
print("inverses:", {a: pow(a, -1, 26) for a in valid})
for a in (2, 13): # not one-to-one: letters collide
print(f"a = {a}: x -> {a}x mod 26 hits", len({a * x % 26 for x in range(26)}), "letters")
try:
encrypt("TEA", 13, 4)
except ValueError as e:
print("refused:", e)আসল আউটপুট (ব্রাউজারের Python):
BAZAR -> NIDIP -> BAZAR | 5^-1 mod 26 = 21
UCVX ZCV UISGU AH PWSC ZA UYLRCZ -> SEND TEN SACKS OF RICE TO SYLHET
valid a: [1, 3, 5, 7, 9, 11, 15, 17, 19, 21, 23, 25]
number of keys: 12 x 26 = 312
inverses: {1: 1, 3: 9, 5: 21, 7: 15, 9: 3, 11: 19, 15: 7, 17: 23, 19: 11, 21: 5, 23: 17, 25: 25}
a = 2: x -> 2x mod 26 hits 13 letters
a = 13: x -> 13x mod 26 hits 2 letters
refused: a = 13: gcd(a, 26) = 13, not a valid key
প্রথম লাইন দুই stepper-এর ফল মেলায়। দ্বিতীয় লাইনে ফাঁকা জায়গা আর শব্দের দৈর্ঘ্য অক্ষত আছে, আর S সবসময় U, E সবসময় C: শব্দের আকার আর অক্ষরের পুনরাবৃত্তি দুটোই ciphertext-এ দেখা যায়। বৈধ a ঠিক ১২টি, মোট key 312. inverse-এর তালিকায় জোড়া দেখুন: 3 আর 9 একে অপরের inverse (27 ≡ 1), 7 আর 15 (105 ≡ 1), আর 25 নিজের inverse, কারণ 25 ≡ −1. a = 2 বর্ণমালাকে ১৩টি অক্ষরে আর a = 13 মাত্র ২টিতে নামায়, উপপাদ্য ৩.২-এর (⇒) অংশ যেমন বলে। শেষ লাইনে কোডটি অবৈধ key প্রত্যাখ্যান করেছে; উপপাদ্যটি কোডে একটি পাহারা হয়ে বসেছে।
৪ · ভাঙার দুই পথ — brute force আর frequency analysis
Kerckhoffs-এর নীতি মেনে ধরুন আক্রমণকারী জানেন বার্তাটি shift বা affine cipher-এ লেখা, শুধু key জানেন না, আর তাঁর হাতে শুধু ciphertext. প্রথম পথটি সবচেয়ে সোজা: সব key একে একে চেষ্টা করা, যাকে brute force বা exhaustive key search বলে। shift-এ ২৬টি, affine-এ ৩১২টি key, কম্পিউটারে চোখের পলক; শুধু সঠিক key অর্থবহ ইংরেজি দেয়।
|𝓚| যদি এত ছোট হয় যে সব key চেষ্টা করা যায়, তবে cipher নিরাপদ নয়। উল্টোটি সত্য নয়: বড় key space নিরাপত্তার প্রয়োজনীয় শর্ত, যথেষ্ট নয়।
দ্বিতীয় অংশের সেরা উদাহরণ সাধারণ substitution cipher: বর্ণমালার যেকোনো একটি permutation-ই key, যেমন A → Q, B → W, …; key-এর সংখ্যা 26! ≈ 4.03 × 1026, brute force-এর নাগালের অনেক বাইরে। তবু এটি কাগজ-কলমে ভাঙা যায়, কারণ shift, affine আর সাধারণ substitution, তিনটিরই একই দুর্বলতা আছে: একই plaintext অক্ষর সবসময় একই ciphertext অক্ষর দেয় (এদের বলে monoalphabetic cipher). তাই ভাষার পরিসংখ্যান ciphertext-এ অক্ষত থাকে।
Frequency analysis
ইংরেজি লেখায় সব অক্ষর সমান ঘন নয়। প্রচলিত একটি টেবিল অনুযায়ী বড় ইংরেজি লেখায় E প্রায় ১২.৭%, T প্রায় ৯.১%, A প্রায় ৮.২%, তারপর O, I, N, S, H, R; আর Q, Z, X, J প্রত্যেকে এক শতাংশের অনেক নিচে (সঠিক সংখ্যা লেখার সংগ্রহ অনুযায়ী একটু বদলায়)। Monoalphabetic cipher অক্ষরের নাম বদলায়, কিন্তু কোন নাম কতবার আসে তা বদলায় না। তাই ciphertext-এর অক্ষর গুনে বড় থেকে ছোট সাজালে ভাষার টেবিলের সঙ্গে মিলিয়ে key আন্দাজ করা যায়। এই কৌশলের নাম frequency analysis. এর সবচেয়ে পুরোনো জানা লিখিত বর্ণনা নবম শতাব্দীর আরব দার্শনিক al-Kindi-র একটি পাণ্ডুলিপিতে বলে সাধারণত ধরা হয়।
Shift cipher-এ একটি অক্ষর-জোড়ার অনুমানই যথেষ্ট: ciphertext-এর সবচেয়ে ঘন অক্ষর y হলে অনুমান y = Ek(E) = 4 + k, অর্থাৎ k ≡ y − 4 (mod 26). নিচের stepper আমাদের নিজের একটি বাক্যে, key না জেনে, ঠিক এটি করে।
1ZLUK AOL NYLLU ALH2JYHALZ AV AOL ZFSOLA3KLWVA ILMVYL AOYLL4গোনা: L 13, A 8, O 45L = 11, E = 4 ⇒ k ≡ 76A: 0 − 7 ≡ 19 = T ✓7SEND THE GREEN TEA …
| অক্ষর গোনা | |
| অনুমান | |
| k |
- শুধু ciphertext. আক্রমণকারীর হাতে ৪৮ অক্ষরের এই লেখা, আর জানা আছে এটি একটি shift cipher. key জানা নেই।
- অক্ষর গোনা। সবচেয়ে ঘন L (13 বার), তারপর A (8 বার), তারপর O আর Y (4 বার করে)।
- প্রথম অনুমান। ইংরেজিতে সবচেয়ে ঘন E, তাই অনুমান L ↔ E: L = 11, E = 4, তাই k ≡ 11 − 4 = 7 (mod 26).
- দ্বিতীয় অক্ষরে যাচাই। k = 7 হলে A-এর plaintext 0 − 7 ≡ 19 = T, যা ইংরেজির দ্বিতীয় ঘন অক্ষর। দুটি স্বাধীন সূত্র একই k দিল।
- Decrypt. সব অক্ষর থেকে 7 বিয়োগ: SEND THE GREEN TEA CRATES TO THE SYLHET DEPOT BEFORE THREE. অর্থবহ ইংরেজি, তাই k = 7 নিশ্চিত।
ছোট লেখায় সবচেয়ে ঘন অক্ষর সবসময় E নাও হতে পারে; তখন দ্বিতীয় বা তৃতীয় অনুমান চেষ্টা করতে হয়। একটি পদ্ধতিগত উপায় হলো প্রতিটি k-এর জন্য decrypt করা লেখার অক্ষর-গণনা ভাষার টেবিল থেকে কত দূরে, তা একটি সংখ্যায় মাপা। সবচেয়ে প্রচলিত মাপ chi-squared দূরত্ব: যদি লেখায় N অক্ষর থাকে, অক্ষর i এসেছে ni বার, আর ভাষায় তার ঘনত্ব pi, তবে
χ2 = ∑i=025 (ni − N pi)2 / (N pi)
সঠিক k-এ লেখাটি ইংরেজির মতো, তাই χ2 ছোট; ভুল k-এ অক্ষরগুলো ভুল জায়গায়, তাই χ2 অনেক বড়। নিচের ব্লক বাজারঘরের গুদাম নিয়ে আমাদের নিজের লেখা একটি অনুচ্ছেদ k = 7 দিয়ে encrypt করে, তারপর key না জেনে অক্ষর গোনে, ২৬টি key-এর brute force দেখায়, আর χ2 দিয়ে সেরা k বাছে।
from collections import Counter
TEXT = """Every morning the warehouse team at Bazarghar checks the orders
that came in during the night. The manager reads each order code aloud,
and two members of staff pack rice, lentils, tea and sugar into sacks.
When the delivery van leaves for Sylhet or Rajshahi, the driver carries
a sealed list of the orders. The list is not really secret; it is only
an example of a message that we want to hide from a curious reader.
Here we encrypt such a message with old ciphers and then break them
again, to see for ourselves why the old methods fail."""
A = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
F = [82, 15, 28, 43, 127, 22, 20, 61, 70, 2, 8, 40, 24, # English letter
67, 75, 19, 1, 60, 63, 91, 28, 10, 24, 2, 20, 1] # frequency per 1000
def nums(s): return [A.index(c) for c in s.upper() if c in A]
def word(xs): return "".join(A[x] for x in xs)
def shift(xs, k): return [(x + k) % 26 for x in xs]
def chi2(xs): # distance from English letter frequencies
n, cnt = len(xs), Counter(xs)
return sum((cnt[i] - n * F[i] / 1000) ** 2 / (n * F[i] / 1000) for i in range(26))
P = nums(TEXT)
C = shift(P, 7) # our own secret: shift by 7
print(len(P), "letters; top plaintext:", Counter(word(P)).most_common(5))
print("top ciphertext letters:", Counter(word(C)).most_common(5))
print("brute force, first 10 letters for every key k:")
for row in range(0, 26, 3):
print(" ".join(f"k={k:<2} {word(shift(C[:10], -k))}" for k in range(row, min(row + 3, 26))))
best = sorted((round(chi2(shift(C, -k)), 1), k) for k in range(26))[:3]
print("lowest chi-squared (score, k):", best)
print("decrypted:", word(shift(C, -best[0][1]))[:40], "...")আসল আউটপুট (ব্রাউজারের Python):
429 letters; top plaintext: [('E', 64), ('A', 43), ('R', 37), ('T', 35), ('S', 32)]
top ciphertext letters: [('L', 64), ('H', 43), ('Y', 37), ('A', 35), ('Z', 32)]
brute force, first 10 letters for every key k:
k=0 LCLYFTVYUP k=1 KBKXESUXTO k=2 JAJWDRTWSN
k=3 IZIVCQSVRM k=4 HYHUBPRUQL k=5 GXGTAOQTPK
k=6 FWFSZNPSOJ k=7 EVERYMORNI k=8 DUDQXLNQMH
k=9 CTCPWKMPLG k=10 BSBOVJLOKF k=11 ARANUIKNJE
k=12 ZQZMTHJMID k=13 YPYLSGILHC k=14 XOXKRFHKGB
k=15 WNWJQEGJFA k=16 VMVIPDFIEZ k=17 ULUHOCEHDY
k=18 TKTGNBDGCX k=19 SJSFMACFBW k=20 RIRELZBEAV
k=21 QHQDKYADZU k=22 PGPCJXZCYT k=23 OFOBIWYBXS
k=24 NENAHVXAWR k=25 MDMZGUWZVQ
lowest chi-squared (score, k): [(25.1, 7), (1081.6, 18), (1446.3, 13)]
decrypted: EVERYMORNINGTHEWAREHOUSETEAMATBAZARGHARC ...
প্রথম দুই লাইনে দেখুন, ciphertext-এর গণনাগুলো (64, 43, 37, 35, 32) plaintext-এর গণনার হুবহু একই সংখ্যা, শুধু অক্ষরের নাম সাত ঘর সরে গেছে: এটিই monoalphabetic cipher-এর দুর্বলতা। এই লেখায় দ্বিতীয় ঘন অক্ষর T নয়, A; ছোট লেখায় টেবিলের ক্রম হুবহু মেলে না, কিন্তু E-এর অনুমান একাই k ≡ L − E = 7 দেয়। Brute force-এর ২৬টি সারির মধ্যে শুধু k = 7-এ পড়ার মতো শব্দ (EVERYMORNI…), আর χ2 সেটি নিজেই বেছেছে: সঠিক k-এর দূরত্ব 25.1, পরের সেরা 1081.6, অর্থাৎ প্রায় ৪০ গুণ দূরে। কোনো মানুষকে পড়ে দেখতে হয়নি।
Affine cipher-এ: দুটি অনুমান, একটি linear congruence-এর জোড়া
Affine-এ দুটি অজানা, a আর b, তাই দুটি অক্ষর-জোড়ার অনুমান লাগে। ধরুন ciphertext-এ সবচেয়ে ঘন T আর তারপর M, আর অনুমান E → T, T → M. তাহলে 4a + b ≡ 19 আর 19a + b ≡ 12 (mod 26). বিয়োগ করলে 15a ≡ −7 ≡ 19. gcd(15, 26) = 1, আর 15−1 ≡ 7 (15 · 7 = 105 ≡ 1), তাই a ≡ 7 · 19 = 133 ≡ 3, আর b ≡ 19 − 4 · 3 = 7. পরীক্ষা: gcd(3, 26) = 1, key বৈধ। আমাদের scratch হিসাবে ৩১২টি key-এর মধ্যে শুধু (3, 7) এই দুই শর্ত মানে। এই ধরনের আক্রমণ, যেখানে কিছু plaintext-ciphertext জোড়া জানা বা অনুমিত, তাকে known-plaintext attack বলে। Affine ফাংশন linear, তাই এমন দুটি জোড়াই যথেষ্ট; আধুনিক cipher-এর নকশার একটি বড় লক্ষ্য হলো লক্ষ লক্ষ জোড়া জেনেও key বের না হওয়া।
৫ · Vigenère cipher — key-শব্দ দিয়ে বদলানো shift
Frequency analysis ঠেকানোর স্বাভাবিক ভাবনা: একই অক্ষর যেন সবসময় একই অক্ষরে না যায়। Vigenère cipher-এ key একটি শব্দ, ধরুন SYLHET, অর্থাৎ shift-এর ক্রম (18, 24, 11, 7, 4, 19). plaintext-এর প্রথম অক্ষর 18 ঘর, দ্বিতীয়টি 24 ঘর, …, সপ্তম অক্ষর আবার 18 ঘর সরে।
key k = (k0, …, km−1) ∈ ℤ26m, যেখানে m হলো key-এর দৈর্ঘ্য। plaintext x0x1x2⋯-এর জন্য
yi = (xi + ki mod m) mod 26, xi = (yi − ki mod m) mod 26
key space-এর আকার 26m: m = 6-এ 308,915,776, m = 10-এ প্রায় 1.4 × 1014.
এখন একই E কখনো W, কখনো C, কখনো P হতে পারে, অবস্থানের ওপর নির্ভর করে। একাধিক বর্ণমালা পালাক্রমে ব্যবহার করে বলে একে polyalphabetic cipher বলে। ছোট উদাহরণ: SENDRICE, key BAG (1, 0, 6): S + 1 = T, E + 0 = E, N + 6 = T, D + 1 = E, R + 0 = R, I + 6 = O, C + 1 = D, E + 0 = E, ফল TETERODE. লক্ষ করুন, ciphertext-এর দুটি T এসেছে দুটি ভিন্ন অক্ষর থেকে (S আর N), আর তিনটি E-এর দুটি এসেছে E থেকে, একটি D থেকে: একটি ciphertext অক্ষরের পেছনে আর কোনো নির্দিষ্ট plaintext অক্ষর থাকে না।
পদ্ধতিটি ষোড়শ শতকের ইতালিতে Giovan Battista Bellaso ১৫৫৩ সালে বর্ণনা করেন বলে উল্লেখ করা হয়; নামটি পরে ভুলভাবে ফরাসি কূটনীতিক Blaise de Vigenère-এর নামে বসে যায়, যিনি ১৫৮৬ সালে একটি শক্তিশালী রূপ লিখেছিলেন। প্রায় তিন শতাব্দী ধরে একে “le chiffre indéchiffrable”, অর্থাৎ অভেদ্য cipher বলা হতো।
দুর্বলতা: key পুনরাবৃত্ত হয়
মূল পর্যবেক্ষণটি সরল। যদি key-এর দৈর্ঘ্য m জানা থাকে, তবে অবস্থান j, j + m, j + 2m, …-এর অক্ষরগুলো (একটি “কলাম”) সবাই একই shift kj পেয়েছে। প্রতিটি কলাম তাই একটি সাধারণ shift cipher, আর §৪-এর frequency analysis সেটি ভাঙে। পুরো সমস্যা তাই নেমে আসে একটি প্রশ্নে: m কত? এর দুটি ক্লাসিক উত্তর আছে।
Kasiski-র পদ্ধতি। plaintext-এ একই শব্দাংশ (যেমন “THE”) বারবার আসে। দুটি ঘটনার দূরত্ব যদি m-এর গুণিতক হয়, তবে দুটিই key-এর একই অংশ দিয়ে encrypt হয়, আর ciphertext-এও একই তিন অক্ষর দেখা যায়। তাই ciphertext-এ পুনরাবৃত্ত তিন অক্ষরের দূরত্বগুলো বেশিরভাগ m-এর গুণিতক হয়, আর তাদের সাধারণ উৎপাদক m-এর দিকে ইঙ্গিত করে। কিছু পুনরাবৃত্তি কাকতালীয়, তাই gcd সরাসরি না নিয়ে দেখা হয় কোন সংখ্যা বেশিরভাগ দূরত্বকে ভাগ করে। এই Kasiski examination Friedrich Kasiski ১৮৬৩ সালে প্রকাশ করেন; Charles Babbage এর আগেই (১৮৫০-এর দশকে) একই কৌশল ব্যবহার করেছিলেন কিন্তু প্রকাশ করেননি বলে ইতিহাসবিদরা উল্লেখ করেন।
Index of coincidence. দ্বিতীয় পদ্ধতিটি গণনার, আর পুরোপুরি ডিসক্রিট ম্যাথের।
N অক্ষরের একটি লেখায় অক্ষর i এসেছে ni বার। লেখা থেকে এলোমেলোভাবে দুটি ভিন্ন অবস্থান বাছলে দুটিতে একই অক্ষর পাওয়ার সম্ভাবনা
IC = ∑i=025 ni(ni − 1) / (N(N − 1))
একে index of coincidence বলে।
সূত্রটি গণনা থেকে আসে: দুটি অবস্থান বাছার C(N, 2) = N(N − 1)/2 উপায়, আর দুটিই অক্ষর i হওয়ার C(ni, 2) উপায়; ভাগ করলে 2 কেটে যায়। ছোট উদাহরণ: BANANA-তে A 3, N 2, B 1, তাই IC = (6 + 2 + 0)/30 = 8/30 ≈ 0.267. বড় লেখায় IC প্রায় ∑ pi2. সব অক্ষর সমান ঘন হলে তা 26 · (1/26)2 = 1/26 ≈ 0.0385, যা এই যোগফলের সর্বনিম্ন মান (Cauchy–Schwarz অসমতা থেকে)। ইংরেজির অসম বিভাজনে এটি প্রায় 0.065–0.067; উপরের টেবিল থেকে হিসাব করলে প্রায় 0.065.
যেকোনো monoalphabetic cipher-এ (shift, affine বা সাধারণ substitution) ciphertext-এর IC plaintext-এর IC-এর সমান।
প্রমাণ:
- cipher একটি bijection σ : ℤ26 → ℤ26, প্রতিটি অবস্থানে একই। plaintext-এ অক্ষর i যতবার আসে, ciphertext-এ σ(i) ঠিক ততবার আসে: n′σ(i) = ni. — bijection গণনা রক্ষা করে
- N বদলায় না, আর ∑j n′j(n′j − 1) = ∑i n′σ(i)(n′σ(i) − 1) = ∑i ni(ni − 1), কারণ σ সূচিগুলোর শুধু ক্রম বদলায়। ∎ — যোগফলের পুনর্বিন্যাস, পর্ব ১৭
এই উপপাদ্য থেকে পরীক্ষাটি আসে। m সঠিক হলে প্রতিটি কলাম একটি shift cipher, তাই প্রতিটি কলামের IC ইংরেজির মতো, প্রায় 0.066. m ভুল হলে একটি কলামে কয়েকটি ভিন্ন shift মিশে যায়, আর IC 1/26-এর দিকে নেমে আসে। তাই m = 1, 2, 3, … চেষ্টা করে কলামগুলোর গড় IC দেখুন; যেখানে হঠাৎ লাফ দেয়, সেটিই m (বা তার গুণিতক)। William F. Friedman ১৯২০-এর দশকের শুরুতে এই ধারণা প্রকাশ করেন বলে উল্লেখ করা হয়।
নিচের ব্লক আগের ব্লকের সঙ্গে একই namespace ব্যবহার করে (data-shared), তাই একই অনুচ্ছেদ, nums, shift আর chi2 আবার কাজে লাগে; আগের ব্লক না চালালে সেটি আগে নিজে চলে। এটি অনুচ্ছেদটি key SYLHET দিয়ে encrypt করে, Kasiski আর IC দিয়ে m বের করে, আর প্রতিটি কলামে χ2 দিয়ে key-এর অক্ষর খোঁজে।
def ic(xs): # index of coincidence
n, cnt = len(xs), Counter(xs)
return sum(v * (v - 1) for v in cnt.values()) / (n * (n - 1))
K = nums("SYLHET") # our own Vigenere key
V = [(x + K[i % len(K)]) % 26 for i, x in enumerate(P)]
print("ciphertext:", word(V)[:36], "...")
print(f"IC: plaintext {ic(P):.4f}, Vigenere {ic(V):.4f}, uniform {1/26:.4f}")
seen, dist = {}, [] # Kasiski: distances between repeats
for i in range(len(V) - 2):
t = tuple(V[i:i + 3])
if t in seen: dist.append(i - seen[t])
seen[t] = i
print(len(dist), "repeated trigrams; how many distances m divides:",
{m: sum(d % m == 0 for d in dist) for m in range(2, 9)})
for m in range(1, 9): # Friedman-style test: IC of columns
print(f" m={m}: mean column IC {sum(ic(V[j::m]) for j in range(m)) / m:.4f}")
m = next(m for m in range(1, 9) if sum(ic(V[j::m]) for j in range(m)) / m > 0.06)
key = [min(range(26), key=lambda k: chi2(shift(V[j::m], -k))) for j in range(m)]
plain = [(x - key[i % m]) % 26 for i, x in enumerate(V)]
print("key length", m, "| recovered key:", word(key), "|", word(plain)[:24], "...")আসল আউটপুট (ব্রাউজারের Python):
ciphertext: WTPYCFGPYPRZLFPDEKWFZBWXLCLTEMTYKHVZ ...
IC: plaintext 0.0700, Vigenere 0.0429, uniform 0.0385
22 repeated trigrams; how many distances m divides: {2: 20, 3: 20, 4: 7, 5: 7, 6: 19, 7: 1, 8: 6}
m=1: mean column IC 0.0429
m=2: mean column IC 0.0485
m=3: mean column IC 0.0520
m=4: mean column IC 0.0474
m=5: mean column IC 0.0421
m=6: mean column IC 0.0663
m=7: mean column IC 0.0424
m=8: mean column IC 0.0482
key length 6 | recovered key: SYLHET | EVERYMORNINGTHEWAREHOUSE ...
Vigenère লেখার IC (0.0429) প্রায় সমান-বিভাজনের 0.0385-এর কাছে নেমে গেছে: একক অক্ষর-গণনা আর key বলে না। Kasiski-র লাইনে ২২টি পুনরাবৃত্তির দূরত্বের ২০টি 2 দিয়ে, ২০টি 3 দিয়ে আর ১৯টি 6 দিয়ে বিভাজ্য, কিন্তু 4 বা 5 দিয়ে মাত্র ৭টি: সংকেত 6 (আর তার উৎপাদক 2, 3). IC-এর টেবিলে m = 6-এ গড় হঠাৎ 0.0663, ইংরেজির মান; m = 2 আর 3-এ সামান্য বাড়ে, কারণ সেই কলামগুলোতে ছয়টির জায়গায় তিনটি বা দুটি shift মেশে। শেষ লাইনে প্রতিটি কলামের χ2 পুরো key SYLHET ফেরত এনেছে। ৪২৯ অক্ষরেই একটি “অভেদ্য” cipher ভেঙেছে; লেখা ছোট বা key লম্বা হলে প্রতিটি কলামে অক্ষর কমে, আর পদ্ধতিটি অনিশ্চিত হয়।
৬ · One-time pad — গণিতে প্রমাণিত গোপনীয়তা
Vigenère ভাঙল কারণ key পুনরাবৃত্ত হয়। যদি key বার্তার সমান লম্বা হয়, প্রতিটি অক্ষর পুরো এলোমেলোভাবে বাছা হয়, আর key কখনো আবার ব্যবহৃত না হয়? তখন কলাম বলে কিছু থাকে না, প্রতিটি অক্ষর নিজের আলাদা shift পায়। এই cipher-এর নাম one-time pad. কম্পিউটারে একে সাধারণত bit-এ লেখা হয়: বার্তা m, key k দুটোই n bit, আর c = m ⊕ k, যেখানে ⊕ হলো XOR (bit-এর যোগ mod 2). Decryption একই: c ⊕ k = m ⊕ k ⊕ k = m, কারণ k ⊕ k = 0.
এখানে “নিরাপদ” কথাটির নির্ভুল অর্থ দেওয়া যায়, সম্ভাবনার ভাষায়: ciphertext দেখার আগে আক্রমণকারীর plaintext নিয়ে যে ধারণা, দেখার পরে তা কি একটুও বদলায়? শর্তসাপেক্ষ সম্ভাবনা আর Bayes-এর উপপাদ্যের পূর্ণ আলোচনা পর্ব ৩৯ “শর্তসাপেক্ষ সম্ভাবনা, independence আর Bayes-এর উপপাদ্য”-এ আসবে; এখানে শুধু সংজ্ঞাটুকু লাগবে।
key K সমসম্ভাব্যভাবে 𝓚 থেকে বাছা, আর বার্তা M থেকে স্বাধীন। cipher-টি perfectly secret, যদি 𝓜-এর ওপর যেকোনো সম্ভাবনা-বিভাজনে, প্রতিটি m আর P(C = c) > 0 এমন প্রতিটি c-এর জন্য
P(M = m | C = c) = P(M = m)
অর্থাৎ ciphertext দেখে plaintext সম্পর্কে নতুন কিছুই জানা যায় না।
(ক) একটি মাত্র অক্ষর, key k সমসম্ভাব্যভাবে ℤ26 থেকে: shift cipher perfectly secret. (খ) n bit-এর বার্তা, key সমসম্ভাব্যভাবে {0, 1}n থেকে, c = m ⊕ k: one-time pad perfectly secret.
প্রমাণ (ক):
- নির্দিষ্ট m আর c-এর জন্য Ek(m) = c ⇔ k ≡ c − m (mod 26): ঠিক একটি key. K সমসম্ভাব্য আর M থেকে স্বাধীন, তাই P(C = c | M = m) = 1/26. — key-এর অনন্যতা
- সম্পূর্ণ সম্ভাবনার সূত্রে P(C = c) = ∑m′ P(M = m′) · 1/26 = 1/26. — সম্ভাবনার যোগফল 1
- Bayes-এর সূত্রে P(M = m | C = c) = P(C = c | M = m) P(M = m) / P(C = c) = (1/26) P(M = m) / (1/26) = P(M = m). ∎ — Bayes
প্রমাণ (খ): হুবহু একই, শুধু ধাপ ১-এ অনন্য key k = c ⊕ m আর সম্ভাবনা 2−n. ∎
প্রমাণের মূলে একটিমাত্র তথ্য: প্রতিটি (m, c) জোড়ার জন্য ঠিক একটি key. তাই ciphertext যা-ই হোক, প্রতিটি plaintext সমানভাবে “সম্ভব”, আর ciphertext কোনো plaintext-এর পক্ষে সাক্ষ্য দেয় না। এর একটি অনিবার্য দামও আছে।
কোনো cipher perfectly secret হলে |𝓚| ≥ |𝓜|.
প্রমাণ:
- M-কে 𝓜-এর ওপর সমসম্ভাব্য ধরুন, আর P(C = c) > 0 এমন একটি c নিন। D(c) = {Dk(c) : k ∈ 𝓚}, অর্থাৎ c থেকে কোনো না কোনো key দিয়ে পাওয়া সম্ভাব্য সব plaintext. — সংজ্ঞা
- |D(c)| ≤ |𝓚|, কারণ প্রতিটি key বড়জোর একটি plaintext দেয়। — ফাংশনের প্রতিচ্ছবি
- |𝓚| < |𝓜| হলে এমন m আছে যা D(c)-তে নেই। কোনো key দিয়েই m থেকে c আসে না, তাই P(M = m | C = c) = 0 ≠ 1/|𝓜| = P(M = m). perfect secrecy ভাঙে। ∎ — pigeonhole, contrapositive
এক অক্ষরের shift-এ |𝓚| = |𝓜| = 26, তাই সমান সমান। কিন্তু দুই অক্ষরের বার্তায় একই key ব্যবহার করলে 26টি key আর 676টি বার্তা: উপপাদ্য ৬.৩ বলে perfect secrecy অসম্ভব। ব্যবহারিক মানে হলো, প্রতিটি নতুন অক্ষরের জন্য নতুন এলোমেলো key চাই, আর key একবার ব্যবহারের পর ফেলে দিতে হবে। নাম “one-time” সেখান থেকে।
Key আবার ব্যবহার করলে যা হয়
ধরুন একই key k দিয়ে দুটি বার্তা পাঠানো হলো: c1 = m1 ⊕ k, c2 = m2 ⊕ k. তাহলে
c1 ⊕ c2 = m1 ⊕ k ⊕ m2 ⊕ k = m1 ⊕ m2
key সম্পূর্ণ উধাও। শুধু দুটি ciphertext থেকেই আক্রমণকারী দুটি বার্তার XOR পান, আর একটি বার্তার কোনো অংশ অনুমান করতে পারলে (যেমন অর্ডার-বার্তা সাধারণত “SEND ”-দিয়ে শুরু হয়) অন্যটির সেই অংশ সরাসরি বেরিয়ে আসে। নিচের ব্লক প্রথমে উপপাদ্য ৬.২(ক)-কে একটি অসম বিভাজনে সব (m, c) গুনে যাচাই করে, তারপর দুই অক্ষরে এক key-এর ব্যর্থতা দেখায়, আর শেষে আমাদের নিজের দুটি বার্তায় key পুনর্ব্যবহারের ফল দেখায়।
import random
from fractions import Fraction
from collections import defaultdict
# 1) one letter, uniform random key k in 0..25: y = (x + k) mod 26
prior = {"A": Fraction(1, 2), "E": Fraction(1, 3), "T": Fraction(1, 6)}
joint = defaultdict(Fraction) # P(M = m and C = c)
for m, p in prior.items():
for k in range(26):
c = (ord(m) - 65 + k) % 26
joint[m, c] += p * Fraction(1, 26)
same = all(joint[m, c] / sum(joint[x, c] for x in prior) == prior[m]
for m in prior for c in range(26))
print("P(M = m | C = c) equals P(M = m) for every m and c:", same)
def ct(w, k): return tuple((ord(ch) - 65 + k) % 26 for ch in w)
cts = {ct(w, k) for w in ("EE", "AT") for k in range(26)} # 2 letters, ONE key
print("2 letters, 1 key: plaintexts fitting each ciphertext:",
{len({w for w in ("EE", "AT") for k in range(26) if ct(w, k) == c}) for c in cts})
# 2) one-time pad on bytes, and what goes wrong when the key is reused
def xor(p, q): return bytes(a ^ b for a, b in zip(p, q))
random.seed(27) # demo only; real keys come from `secrets`
m1, m2 = b"SEND 40 SACKS TO SYLHET", b"PAY THE DRIVER BY NOON."
key = bytes(random.randrange(256) for _ in m1)
c1 = xor(m1, key)
print("c1 =", c1.hex()[:24], "...", "| decrypts to:", xor(c1, key))
c2 = xor(m2, key) # MISTAKE: the same pad used twice
print("c1 xor c2 == m1 xor m2:", xor(c1, c2) == xor(m1, m2))
crib = b"SEND " # a guess about the start of m1
print("guess 'SEND ' for m1 -> m2 starts with:", xor(xor(c1, c2), crib))
print("key space for 23 bytes: 256^23 =", f"{256 ** 23:.2e}", "keys")আসল আউটপুট (ব্রাউজারের Python):
P(M = m | C = c) equals P(M = m) for every m and c: True
2 letters, 1 key: plaintexts fitting each ciphertext: {1}
c1 = a6c8dc200515b28ad2ff8d16 ... | decrypts to: b'SEND 40 SACKS TO SYLHET'
c1 xor c2 == m1 xor m2: True
guess 'SEND ' for m1 -> m2 starts with: b'PAY T'
key space for 23 bytes: 256^23 = 2.45e+55 keys
প্রথম লাইন: A, E, T-এর অসম বিভাজন (1/2, 1/3, 1/6) নিয়েও ২৬টি ciphertext-এর প্রতিটিতে পরবর্তী সম্ভাবনা আগের সম্ভাবনার হুবহু সমান, ভগ্নাংশে নির্ভুল হিসাবে (Fraction, কোনো দশমিকের গোলমাল নেই)। দ্বিতীয় লাইন: EE আর AT দুই বার্তা, একটি key; প্রতিটি ciphertext ঠিক একটি বার্তার সঙ্গে খাপ খায়, কারণ EE-এর দুই অক্ষর সবসময় সমান থাকে আর AT-এর থাকে না। ciphertext দেখামাত্র বার্তা জানা, যা উপপাদ্য ৬.৩-এর বাস্তব রূপ। শেষ চার লাইন: সঠিক ব্যবহারে c1 এলোমেলো bytes, আর key দিয়ে বার্তা ফেরে। কিন্তু key দুবার ব্যবহৃত হতেই c1 ⊕ c2 = m1 ⊕ m2, আর শুধু “SEND ” অনুমান করেই দ্বিতীয় বার্তার শুরু “PAY T” বেরিয়ে এসেছে, key-এর 2.45 × 1055 আকার কোনো কাজে আসেনি। এখানে random.seed(27) শুধু আউটপুট পুনরুৎপাদনের জন্য; আসল key-এর জন্য CSPRNG লাগে (Python-এ secrets), যেমন পর্ব ২৬-এ দেখেছেন।
ইতিহাস: AT&T-এর প্রকৌশলী Gilbert Vernam ১৯১৭ সালে টেলিপ্রিন্টারের জন্য key-টেপের সঙ্গে বার্তা XOR করার যন্ত্র নকশা করেন (patent ১৯১৯), আর Joseph Mauborgne key-কে পুরো এলোমেলো আর একবারের রাখার গুরুত্ব যোগ করেন বলে সাধারণত উল্লেখ করা হয়। গণিতে প্রমাণ আসে পরে: Claude Shannon-এর “Communication Theory of Secrecy Systems” (Bell System Technical Journal, ১৯৪৯), যা যুদ্ধকালীন একটি গোপন প্রতিবেদনের ওপর ভিত্তি করে লেখা। Key পুনর্ব্যবহারের বিপদের ঐতিহাসিক উদাহরণ হিসেবে মার্কিন Venona প্রকল্প প্রায়ই উদ্ধৃত হয়, যেখানে একই pad-এর পাতা একাধিকবার ব্যবহারের ফলে কিছু বার্তা পড়া গিয়েছিল বলে প্রকাশিত হয়েছে।
৭ · Classical থেকে আধুনিক — কী রইল, কী বদলাল
One-time pad নিখুঁত, কিন্তু অব্যবহারিক: বাজারঘর প্রতিদিন যত বাইট পাঠাবে, ঠিক তত বাইট নতুন এলোমেলো key আগে থেকে গোপনে গুদামে পৌঁছাতে হবে। key পৌঁছানোর নিরাপদ পথ থাকলে বার্তাটিই সেই পথে পাঠানো যেত। উপপাদ্য ৬.৩ বলে perfect secrecy চাইলে এই দাম দিতেই হবে। আধুনিক cryptography তাই লক্ষ্য একটু বদলেছে: “কোনো আক্রমণকারী কিছুই জানবে না” থেকে “কোনো বাস্তবসম্মত সময়ে চলা আক্রমণকারী উল্লেখযোগ্য কিছু জানবে না”। তখন ছোট key (যেমন 128 bit) দিয়ে অনেক বড় ডেটা encrypt করা সম্ভব।
এই পর্বের ব্যর্থতাগুলো থেকে আধুনিক নকশার নিয়মগুলো সরাসরি আসে:
- বড় key space: 312 বা 266 নয়, 2128 বা তার বেশি, যাতে brute force অসম্ভব থাকে (নীতি ৪.১).
- পরিসংখ্যান লুকানো: একই plaintext যেন একই ciphertext না দেয়; Shannon-এর ভাষায় confusion আর diffusion, অর্থাৎ key-এর সঙ্গে সম্পর্ক জটিল করা আর প্রতিটি ইনপুট bit-এর প্রভাব সব আউটপুট bit-এ ছড়িয়ে দেওয়া।
- Linear না হওয়া: affine-এর মতো linear সম্পর্ক দুটি জোড়াতেই key দিয়ে দেয়; আধুনিক cipher-এ non-linear অংশ থাকে।
- Key আর nonce কখনো পুনর্ব্যবহার নয়: one-time pad-এর শিক্ষা আজকের stream cipher আর block cipher mode-এও একই।
আজকের প্রধান symmetric cipher AES একটি block cipher: 128 bit-এর একটি block আর 128, 192 বা 256 bit-এর key নিয়ে কয়েক দফা substitution আর মিশ্রণ চালায়। এটি ২০০১ সালে মার্কিন মান FIPS 197 হিসেবে প্রকাশিত হয়, একটি উন্মুক্ত আন্তর্জাতিক প্রতিযোগিতার পর, যা Kerckhoffs-এর নীতির বাস্তব রূপ। AES-এর ভেতরের গণিত GF(28) নামের একটি finite field-এ চলে, যা পর্ব ৩০ “Finite field আর elliptic curve-এর পরিচয়”-এ আসবে। কীভাবে নিরাপদে ব্যবহার করতে হয় (mode, nonce, authenticated encryption), তা Cybersecurity কোর্সের পর্ব ০৮-এ।
আর একটি সমস্যা এখনো রয়ে গেছে: অফিস আর গুদাম শুরুতে key-টি ভাগ করবে কীভাবে, যদি তাদের মধ্যে শুধু খোলা পথ থাকে? এটি “key distribution problem”, আর তার সমাধান public key cryptography, এই মডিউলের পরের তিন পর্বের বিষয়: পর্ব ২৮ “RSA-এর গণিত — key, encryption আর কেন কাজ করে”, পর্ব ২৯ “Discrete logarithm আর Diffie–Hellman key exchange”, আর পর্ব ৩০.
৮ · পরীক্ষার অঙ্ক আর একটি ভুল প্রমাণ
বিশ্ববিদ্যালয়, চাকরি বা ইন্টারভিউর লিখিত পরীক্ষায় এই বিষয়ের যে ধাঁচের অঙ্ক আসে, তার তিনটি পূর্ণ সমাধান। প্রতিটি সংখ্যা scratch-এ Python দিয়ে মেলানো।
অঙ্ক ১: affine key (7, 3) দিয়ে TEA encrypt করুন, তারপর decrypt করে মেলান
- gcd(7, 26) = 1, তাই key বৈধ। T = 19, E = 4, A = 0.
- Encryption: 7 · 19 + 3 = 136 = 5 · 26 + 6 → G; 7 · 4 + 3 = 31 ≡ 5 → F; 7 · 0 + 3 = 3 → D. ciphertext GFD.
- 7−1 mod 26: 26 = 3 · 7 + 5, 7 = 1 · 5 + 2, 5 = 2 · 2 + 1; পেছন থেকে 1 = 5 − 2 · 2 = 3 · 5 − 2 · 7 = 3 · 26 − 11 · 7, তাই 7−1 ≡ −11 ≡ 15 (যাচাই 7 · 15 = 105 ≡ 1).
- Decryption x = 15(y − 3): G = 6 → 15 · 3 = 45 ≡ 19 = T; F = 5 → 15 · 2 = 30 ≡ 4 = E; D = 3 → 0 = A. ফেরত TEA ✓.
অঙ্ক ২: একটি লম্বা affine ciphertext-এর সবচেয়ে ঘন অক্ষর T, দ্বিতীয় M. key বের করুন।
- অনুমান E → T আর T → M: 4a + b ≡ 19 আর 19a + b ≡ 12 (mod 26).
- বিয়োগ: 15a ≡ −7 ≡ 19. 15−1 ≡ 7 (105 ≡ 1), তাই a ≡ 7 · 19 = 133 ≡ 3.
- b ≡ 19 − 4 · 3 = 7. gcd(3, 26) = 1, key (3, 7) বৈধ। যাচাই: 3 · 4 + 7 = 19 = T ✓, 3 · 19 + 7 = 64 ≡ 12 = M ✓. (যদি পাওয়া a জোড় বা 13 হতো, অনুমানটি ভুল ধরে পরের জোড়া চেষ্টা করতে হতো।)
অঙ্ক ৩: Vigenère key BAG দিয়ে SENDRICE encrypt করুন
- key-এর shift: B = 1, A = 0, G = 6, পালাক্রমে 1, 0, 6, 1, 0, 6, 1, 0.
- S(18) + 1 = T, E(4) + 0 = E, N(13) + 6 = 19 = T, D(3) + 1 = E, R(17) + 0 = R, I(8) + 6 = 14 = O, C(2) + 1 = D, E(4) + 0 = E.
- ciphertext TETERODE. লক্ষ করুন, এখানে কোনো অক্ষর 25 পেরোয়নি; পেরোলে 26 বিয়োগ করতে হতো।
প্রমাণের ভুল ধরুন: “দুবার encrypt করলে key space বর্গ হয়”
একজন লিখলেন: “দাবি: প্রথমে affine key (a1, b1), তারপর আলাদা key (a2, b2) দিয়ে encrypt করলে আক্রমণকারীকে 312 · 312 = 97,344টি key-জোড়া চেষ্টা করতে হয়, তাই নিরাপত্তা 312 গুণ বাড়ে। প্রমাণ: প্রতিটি key-জোড়া একটি আলাদা encryption, আর গুণের নিয়মে জোড়া 3122টি। ∎” পড়ে ভুলটি নিজে খুঁজুন।
ভুল কোথায়: “প্রতিটি key-জোড়া একটি আলাদা encryption” কথাটি মিথ্যা। দুই affine ফাংশনের composition আবার একটি affine ফাংশন: a2(a1x + b1) + b2 = (a2a1)x + (a2b1 + b2). আর gcd(a2a1, 26) = 1, তাই এটি ৩১২টি সাধারণ affine key-এর একটি। ৯৭,৩৪৪টি জোড়া আসলে মাত্র ৩১২টি ভিন্ন ফাংশন দেয়; আক্রমণকারী সরাসরি একটি affine key খোঁজেন। উদাহরণ: (7, 3) ∘ (5, 8) = (35 mod 26, 7 · 8 + 3 mod 26) = (9, 7), আর ২৬টি অক্ষরেই দুই পথ একই ফল দেয় (scratch-এ যাচাই করা)। গুণের নিয়ম key-জোড়া গোনে, ভিন্ন ফাংশন নয়; গোনার আগে দেখতে হয় দুটি জিনিস সত্যিই আলাদা কি না।
আরও গভীরে: Affine cipher-এর matrix রূপ Hill cipher (Lester Hill, ১৯২৯): কয়েকটি অক্ষরের একটি vector-কে mod 26-এ একটি invertible matrix দিয়ে গুণ করা হয়; key বৈধ ⇔ determinant-এর সঙ্গে 26-এর gcd = 1, যা উপপাদ্য ৩.২-এর সরাসরি সাধারণীকরণ, আর এটিও known-plaintext attack-এ linear algebra দিয়ে ভাঙে। Shannon-এর ১৯৪৯ সালের কাজ “unicity distance” নামে একটি ধারণাও দেয়: কত অক্ষর ciphertext থাকলে key অনন্যভাবে নির্ধারিত হয়, যা ভাষার redundancy-র ওপর নির্ভর করে। আধুনিক তত্ত্বে perfect secrecy-র জায়গায় computational সংজ্ঞা আসে, যেমন Goldwasser আর Micali-র (১৯৮২) semantic security, যেখানে আক্রমণকারী শুধু polynomial সময়ে চলেন।
পরীক্ষা ও ইন্টারভিউতে: বিশ্ববিদ্যালয়, চাকরি বা ইন্টারভিউর পরীক্ষায় এই ধরনের প্রশ্ন প্রায়ই আসে:
- Affine cipher-এ a = 13 কেন চলে না? gcd(13, 26) = 13 ≠ 1; 13x mod 26 শুধু 0 আর 13, তাই ফাংশন one-to-one নয়, decrypt অসম্ভব।
- Affine key কয়টি? φ(26) · 26 = 12 · 26 = 312; সাধারণভাবে n · φ(n).
- Kerckhoffs-এর নীতি কী? পদ্ধতি শত্রু জানলেও নিরাপত্তা থাকতে হবে; গোপন শুধু key.
- Vigenère-এর key দৈর্ঘ্য কীভাবে বের হয়? পুনরাবৃত্ত অংশের দূরত্বের সাধারণ উৎপাদক (Kasiski), বা কলামের IC প্রায় 0.066 হওয়া (Friedman).
- One-time pad-এর key আবার ব্যবহার করলে? c1 ⊕ c2 = m1 ⊕ m2, key বাদ পড়ে যায়; একটি বার্তার অনুমান অন্যটি খুলে দেয়।
৯ · AI-এর যুগে — cipher-এর হিসাব আর প্রমাণ যাচাই
AI সহকারী classical cipher-এর হিসাব দ্রুত করে দেয়, কিন্তু আত্মবিশ্বাসী অথচ ভুল প্রমাণ (converse প্রমাণ, শূন্য হতে পারে এমন কিছু দিয়ে ভাগ), off-by-one গণনা, শর্তহীন উপপাদ্য, বানানো উৎস আর modular arithmetic-এর ভুল এখানেও নিয়মিত দেখা যায়:
| AI-এর ভুল | কীভাবে ধরবেন |
|---|---|
| A = 1 থেকে গোনা, অথচ সূত্রে mod 26 আর A = 0 ধরা; ফলে প্রতিটি অক্ষর এক ঘর সরে যায় | নিজের এক লাইনের ফাংশনে দেওয়া key দিয়ে encrypt করে উত্তরের সঙ্গে মেলান |
| “a মৌলিক হলেই affine key বৈধ” বা “a বিজোড় হলেই বৈধ” (a = 13 ভুলে যাওয়া) | শর্তটি gcd(a, 26) = 1; ২৬টি a-এর প্রতিটিতে 26টি আউটপুট আলাদা কি না brute force করুন |
| ভুল modular inverse (যেমন 5−1 = 5), বা ঋণাত্মক ভাগশেষ ঠিক না করা | a · a−1 mod 26 = 1 কি না গুনুন; Python-এ pow(a, -1, 26) |
| “ax ≡ ay হলে a কেটে x ≡ y”, শর্ত ছাড়া | কাটা বৈধ শুধু gcd(a, 26) = 1 হলে; a = 2, x = 0, y = 13-এ counterexample দেখুন |
“one-time pad নিরাপদ, তাই key আবার ব্যবহার করা যায়” বা seed-যুক্ত random দিয়ে key বানানো | c1 ⊕ c2 নিজে হিসাব করুন; key-এর উৎস CSPRNG কি না দেখুন |
| বানানো ইতিহাস: ভুল সাল, ভুল আবিষ্কারক, বানানো “বিখ্যাত ঘটনা” | মূল উৎস বা বিশ্বস্ত ইতিহাস-গ্রন্থে মিলিয়ে নিন; নিশ্চিত না হলে দাবিটি বাদ দিন |
AI-এর কাছে cipher-এর হিসাব বা প্রমাণ চাইলে এমন কাঠামো দিন, যাতে প্রতিটি ধাপ আলাদা করে পরীক্ষা করা যায়:
AI-এর উত্তর হাতে পেলে — যাচাইয়ের চেকলিস্ট
এই পর্বের প্রায় প্রতিটি দাবি ২৬টি অক্ষর বা ৩১২টি key-এ এক সেকেন্ডের কম সময়ে brute force করা যায়; AI-এর “সবসময়” বা “কখনো না” পেলে আগে ছোট ক্ষেত্রে counterexample খুঁজুন।
ভাবনার প্রশ্ন
প্রতিটি প্রশ্ন নিয়ে নিজে কিছুক্ষণ ভাবুন, তারপর “→ উত্তর” চাপুন।
প্র ০১Kerckhoffs-এর নীতি বলে পদ্ধতি গোপন রাখার ওপর ভরসা করবেন না। তবু “গোপন algorithm”-এর দিকে ঝোঁক বারবার ফিরে আসে কেন, আর উন্মুক্ত নকশা কীভাবে নিরাপত্তা বাড়ায়?
Auguste Kerckhoffs ১৮৮৩ সালে যখন তাঁর নীতিগুলো লেখেন, তখন টেলিগ্রাফের যুগ: সামরিক বার্তা তারে যায়, আর অনেক অফিসার একই পদ্ধতি ব্যবহার করেন। তাঁর যুক্তি ছিল ব্যবহারিক। একটি পদ্ধতি যত মানুষ আর যন্ত্রের কাছে থাকে, তা তত সহজে শত্রুর হাতে যায়: একজন ধরা পড়লেন, একটি যন্ত্র হারাল, একটি নথি চুরি হলো। পদ্ধতি বদলাতে সব অফিসারকে নতুন করে শেখাতে হয়, কিন্তু key বদলাতে শুধু একটি নতুন সংখ্যা পাঠাতে হয়। তাই নিরাপত্তা এমন জায়গায় রাখা উচিত, যা সস্তায় বদলানো যায়।
তবু গোপন algorithm-এর আকর্ষণ থেকে যায়, কারণ মনে হয় “শত্রু না জানলে তো আরও কঠিন”। সমস্যা হলো, এই অতিরিক্ত বাধা কতটুকু, তা মাপা যায় না, আর একবার ফাঁস হলে সব ব্যবহারকারী একসঙ্গে বিপদে পড়েন। ইতিহাসে বহুবার দেখা গেছে, প্রকাশ না করা মালিকানাধীন cipher বিশ্লেষকরা সফটওয়্যার বা হার্ডওয়্যার উল্টে বিশ্লেষণ করে বের করে ফেলেছেন, আর তারপর দুর্বলতা দ্রুত পাওয়া গেছে; গোপনীয়তা দুর্বল নকশাকে শুধু কিছুদিন আড়াল করে রেখেছিল।
উন্মুক্ত নকশার উল্টো উদাহরণ AES. মার্কিন NIST ১৯৯৭ সালে একটি উন্মুক্ত প্রতিযোগিতা ঘোষণা করে; বিশ্বের নানা দল নকশা জমা দেয়, আর কয়েক বছর ধরে সবাই সবার নকশা আক্রমণ করার চেষ্টা করে। বেলজিয়ামের Joan Daemen আর Vincent Rijmen-এর Rijndael বিজয়ী হয়, আর ২০০১ সালে FIPS 197 হিসেবে প্রকাশিত হয়। নকশা, যুক্তি আর পরীক্ষা সবই প্রকাশ্য, আর তার পরের দুই দশকের বেশি সময়ের বিশ্লেষণে বাস্তবে কাজে লাগার মতো কোনো আক্রমণ প্রকাশিত হয়নি বলে সাধারণভাবে ধরা হয়। এখানে আস্থা আসে বহু চোখের দীর্ঘ পরীক্ষা থেকে, গোপনীয়তা থেকে নয়।
এই পর্বের গণিতেও নীতিটি দেখা যায়: affine cipher-এর সূত্র প্রকাশ্য হওয়ায় আমরা প্রমাণ করতে পারলাম ঠিক ৩১২টি key আছে, আর তাই এটি দুর্বল। একটি গোপন পদ্ধতির নিরাপত্তা নিয়ে এমন নির্ভুল কথা বলা যায় না।
মূল উপলব্ধি: নিরাপত্তা এমন গোপনীয়তার ওপর রাখুন যা ছোট, মাপা যায় আর সহজে বদলানো যায়, অর্থাৎ key; পদ্ধতি প্রকাশ্য থাকলে তার দুর্বলতা খোঁজা আর প্রমাণ করা দুটোই সম্ভব হয়।
প্র ০২Frequency analysis-এর আবিষ্কারের পরেও monoalphabetic cipher শত শত বছর ব্যবহৃত হলো কেন, আর তার দাম কারা দিয়েছেন?
নবম শতাব্দীতে বাগদাদের পণ্ডিত al-Kindi একটি পাণ্ডুলিপিতে অক্ষর গুনে গোপন লেখা পড়ার পদ্ধতি বর্ণনা করেন বলে সাধারণত ধরা হয়; ধারণাটি এসেছিল আরবি ভাষার পাঠ্য নিয়ে ভাষাতাত্ত্বিক চর্চা থেকে। কিন্তু জ্ঞান তখন ধীরে ছড়াত। এক অঞ্চলের পদ্ধতি অন্য অঞ্চলের রাজদরবারে পৌঁছাতে প্রজন্ম লেগে যেত, আর ইউরোপে রেনেসাঁর সময় রাজদরবারগুলো নিজস্ব cipher বিশেষজ্ঞ রাখতে শুরু করে বলে জানা যায়। যাঁরা cipher ব্যবহার করতেন, তাঁরা প্রায়ই জানতেন না যে অন্য পক্ষে কেউ অক্ষর গুনছেন।
দ্বিতীয় কারণ ব্যবহারের সুবিধা। Monoalphabetic cipher হাতে দ্রুত চলে, ভুল কম হয়, আর একটি ছোট টেবিল মুখস্থ রাখলেই হয়। দুর্বলতা ঢাকতে লোকেরা কৌশল যোগ করতেন: “nomenclator”, যেখানে অক্ষর ছাড়াও সাধারণ শব্দ আর নামের জন্য আলাদা চিহ্ন; ফাঁকা চিহ্ন (null), যার কোনো অর্থ নেই; আর বেশি ঘন অক্ষরের জন্য কয়েকটি বিকল্প চিহ্ন। এগুলো কাজ কঠিন করত, কিন্তু মূল পরিসংখ্যান পুরোপুরি মুছত না।
সবচেয়ে বেশি উদ্ধৃত দামের উদাহরণ ১৫৮৬ সালের ইংল্যান্ড। স্কটল্যান্ডের রানি Mary বন্দি অবস্থায় Anthony Babington-এর সঙ্গে একটি nomenclator-এ চিঠি বিনিময় করেন। Elizabeth I-এর মন্ত্রী Francis Walsingham-এর লোক Thomas Phelippes চিঠিগুলো পড়ে ফেলেন, আর সেই প্রমাণে পরের বছর Mary-র মৃত্যুদণ্ড হয়, এমনটিই প্রচলিত ইতিহাস। এখানে শিক্ষাটি নির্মম: একটি দুর্বল cipher শুধু “নিরাপত্তা নেই” নয়, “নিরাপত্তা আছে” এই মিথ্যা বিশ্বাস দেয়, আর মানুষ তখন এমন কথা লেখেন যা খোলা চিঠিতে কখনো লিখতেন না।
গাণিতিক দিক থেকে কারণটি উপপাদ্য ৫.৩-এ: যেকোনো bijection অক্ষর-গণনার বহুসেট (multiset) রক্ষা করে। key space যত বড়ই হোক (26! ≈ 4 × 1026), এই কাঠামোগত তথ্য ciphertext-এ থেকেই যায়, আর বড় key space তা লুকাতে পারে না।
মূল উপলব্ধি: একটি আক্রমণ জানা থাকলেও প্রতিরক্ষা তখনই বদলায় যখন ব্যবহারকারী তা জানেন ও মানেন; আর দুর্বল এনক্রিপশন মিথ্যা নিরাপত্তাবোধ দিয়ে খোলা লেখার চেয়েও বেশি ক্ষতি করতে পারে।
প্র ০৩Vigenère প্রায় তিনশো বছর “অভেদ্য” বলে খ্যাত ছিল। এই খ্যাতি কীভাবে ভাঙল, আর “কেউ এখনো ভাঙেনি” থেকে “নিরাপদ” সিদ্ধান্ত টানা কেন বিপজ্জনক?
Bellaso ১৫৫৩ সালে key-শব্দের ধারণা দেন, আর Blaise de Vigenère ১৫৮৬ সালে একটি শক্তিশালী রূপ (autokey, যেখানে বার্তা নিজেই key-এর অংশ) বর্ণনা করেন বলে উল্লেখ করা হয়। পরে সহজ পুনরাবৃত্ত-key রূপটিই Vigenère-এর নামে পরিচিত হয়। হাতে চালানো তুলনামূলক ধীর আর ভুলপ্রবণ বলে এটি দীর্ঘদিন ব্যাপকভাবে ব্যবহৃত হয়নি, কিন্তু খ্যাতি ছিল: অক্ষর গুনে একে ভাঙা যায় না, কারণ একক অক্ষর-গণনা প্রায় সমান হয়ে যায়, যা আমাদের ব্লকে IC 0.0429 দেখিয়েছে।
ভাঙার মূল ধারণা হলো সমস্যাটিকে দুই ভাগে ভাগ করা: আগে key-এর দৈর্ঘ্য, তারপর প্রতিটি কলাম আলাদা shift cipher. ১৮৫০-এর দশকে Charles Babbage একটি চ্যালেঞ্জের জবাবে এটি করেছিলেন বলে তাঁর কাগজপত্র থেকে জানা যায়, কিন্তু তিনি প্রকাশ করেননি; প্রুশিয়ার সেনা কর্মকর্তা Friedrich Kasiski ১৮৬৩ সালে স্বাধীনভাবে পদ্ধতিটি বই আকারে প্রকাশ করেন। বিংশ শতকের শুরুতে William Friedman index of coincidence দিয়ে কাজটিকে পরিসংখ্যানের ভিত্তিতে দাঁড় করান: একটি সংখ্যা (IC) দেখেই বলা যায় একটি অক্ষর-ধারা monoalphabetic কি না, আর key-এর দৈর্ঘ্য কত। আমাদের ব্লকে m = 6-এ গড় IC 0.0429 থেকে লাফ দিয়ে 0.0663-এ উঠেছে।
এই ইতিহাসের সবচেয়ে বড় শিক্ষা যুক্তিবিদ্যার। “কেউ ভাঙতে পারেনি” একটি অস্তিত্বের দাবির অনুপস্থিতি, প্রমাণ নয়: হয়তো কেউ ভাঙেননি, হয়তো ভেঙেছেন কিন্তু বলেননি (Babbage-এর মতো, বা কোনো সরকারি দপ্তর)। Cryptography-তে প্রতিপক্ষ তার সাফল্য সাধারণত গোপন রাখে। তাই আধুনিক cryptography নিরাপত্তার কথা বলে দুইভাবে: হয় গাণিতিক প্রমাণ (one-time pad-এর মতো, বা “এই cipher ভাঙলে একটি সুপরিচিত কঠিন সমস্যা সমাধান হয়ে যায়” ধরনের reduction), নয়তো দীর্ঘ উন্মুক্ত বিশ্লেষণ। আর শিক্ষাটি নিয়মিত মনে করিয়ে দেওয়া হয়: একটি নকশা সফলভাবে ভাঙার চেষ্টার পরিমাণই তার প্রতি আস্থার মাপ।
একটি গাণিতিক টীকাও আছে। key যদি বার্তার সমান লম্বা আর পুরো এলোমেলো হয়, তবে প্রতিটি কলামে একটি মাত্র অক্ষর, আর Vigenère হয়ে যায় one-time pad, যার perfect secrecy প্রমাণিত। দুর্বলতা তাই পদ্ধতির গঠনে নয়, key-এর পুনরাবৃত্তিতে।
মূল উপলব্ধি: আক্রমণ খুঁজে না পাওয়া নিরাপত্তার প্রমাণ নয়; গঠনের দুর্বলতা (এখানে key-এর পর্যায়ক্রম) একবার চিহ্নিত হলে বড় key space-ও রক্ষা করে না।
প্র ০৪One-time pad গণিতে নিখুঁত, তবু আজকের ইন্টারনেট এটি ব্যবহার করে না। কেন, আর এই সমস্যা থেকে কোন নতুন ধারণার জন্ম হলো?
Shannon-এর উপপাদ্য (উপপাদ্য ৬.৩) one-time pad-এর দামটি নির্ভুলভাবে বলে: perfect secrecy চাইলে key অন্তত বার্তার সমান বড় হতে হবে। একটি ভিডিও কলে প্রতি সেকেন্ডে লক্ষ লক্ষ bit যায়; one-time pad-এ ঠিক ততটুকু এলোমেলো key আগেই দুই পক্ষের কাছে নিরাপদে পৌঁছাতে হবে, আর একবার ব্যবহারের পর নষ্ট করতে হবে। এই key পৌঁছানোর জন্য যদি একটি নিরাপদ পথ থাকেই, তবে বার্তাটিই সেই পথে পাঠানো যেত। One-time pad তাই গোপনীয়তার সমস্যাকে মুছে দেয় না, তাকে key বিতরণের সমস্যায় সরিয়ে দেয়।
বাস্তবে আরও দুটি সমস্যা আছে। প্রথমত, key সত্যিই এলোমেলো হতে হবে; কোনো pseudorandom সূত্র থেকে এলে উপপাদ্যের অনুমান ভাঙে, আর পর্ব ২৬-এ দেখেছেন LCG তিনটি আউটপুটেই ধরা পড়ে। দ্বিতীয়ত, key কখনো আবার ব্যবহার করা চলবে না, আর মানুষের প্রতিষ্ঠানে এই নিয়ম মানা কঠিন। মার্কিন Venona প্রকল্পের প্রকাশিত ইতিহাস অনুযায়ী, যুদ্ধকালীন চাপে একই pad-এর কিছু পাতা একাধিকবার তৈরি ও ব্যবহৃত হয়েছিল, আর ঠিক আমাদের ব্লকের c1 ⊕ c2 = m1 ⊕ m2 সম্পর্ক দিয়ে বিশ্লেষকরা বহু বছর ধরে কিছু বার্তা আংশিকভাবে পড়েছিলেন।
এই কারণে one-time pad সীমিত কিছু জায়গায়, যেমন উচ্চপর্যায়ের কূটনৈতিক বা সামরিক যোগাযোগে, ঐতিহাসিকভাবে ব্যবহৃত হয়েছে বলে জানা যায়, কিন্তু সাধারণ যোগাযোগে নয়। আধুনিক cryptography দুটি পথে এগিয়েছে। এক, নিরাপত্তার সংজ্ঞা বদল: “সম্ভাবনার নিখুঁত সমতা” থেকে “বাস্তবসম্মত সময়ে চলা কোনো আক্রমণকারী পার্থক্য করতে পারবে না”। তাতে 128 bit-এর একটি key দিয়ে বিশাল ডেটা encrypt করা যায় (AES-এর মতো block cipher আর stream cipher). দুই, key বিতরণের সমস্যার নতুন সমাধান: ১৯৭৬ সালে Whitfield Diffie আর Martin Hellman দেখান, দুই পক্ষ খোলা পথে কথা বলেই একটি গোপন key-তে একমত হতে পারে, যার নিরাপত্তা discrete logarithm-এর কঠিনতার ওপর দাঁড়িয়ে। এটি পর্ব ২৯ “Discrete logarithm আর Diffie–Hellman key exchange”-এর বিষয়, আর RSA আসছে পর্ব ২৮-এ।
মূল উপলব্ধি: নিখুঁত নিরাপত্তার দাম key-এর আকার, যা Shannon প্রমাণ করেছেন; সেই দাম না দিয়ে কাজ চালাতেই computational নিরাপত্তা আর public key cryptography-র জন্ম।
প্র ০৫দ্বিতীয় বিশ্বযুদ্ধের Enigma যন্ত্র ভাঙায় গণিতবিদদের ভূমিকা কী ছিল, আর এই পর্বের ধারণাগুলো সেখানে কোথায় দেখা যায়?
Enigma একটি বৈদ্যুতিক-যান্ত্রিক cipher যন্ত্র, যা জার্মান সেনাবাহিনী ব্যবহার করত। গাণিতিকভাবে প্রতিটি অক্ষর চাপার সময় যন্ত্রটি বর্ণমালার একটি permutation প্রয়োগ করে, আর প্রতিবার চাপার পর rotor ঘুরে যায় বলে পরের অক্ষরে permutation বদলায়। অর্থাৎ এটি একটি খুব দীর্ঘ পর্যায়ের polyalphabetic cipher, Vigenère-এর যান্ত্রিক উত্তরসূরি। এর একটি বিশেষ গঠনও ছিল: reflector-এর কারণে প্রতিটি permutation নিজের inverse, আর কোনো অক্ষর কখনো নিজের কাছে যায় না।
পোল্যান্ডের সাইফার ব্যুরোতে তরুণ গণিতবিদ Marian Rejewski ১৯৩২ সালের শেষ দিকে permutation-এর তত্ত্ব ব্যবহার করে যন্ত্রের ভেতরের তারের সংযোগ পুনর্গঠন করেন বলে ইতিহাসে লেখা আছে; তাঁর সঙ্গে কাজ করেন Jerzy Różycki আর Henryk Zygalski. মূল কৌশল ছিল permutation-কে চক্রে (cycle) ভেঙে দেখা: দুটি permutation-এর গুণফলের চক্রের দৈর্ঘ্যের কাঠামো plugboard-এর সেটিং বদলালেও একই থাকে, ঠিক যেমন উপপাদ্য ৫.৩-এ bijection অক্ষর-গণনার বহুসেট রক্ষা করে। তাই একটি বদলায় না এমন রাশি (invariant) দিয়ে বিশাল সম্ভাবনার জগৎকে ছোট করা গেল। ১৯৩৯ সালের জুলাইয়ে পোলিশ দল তাদের ফলাফল ব্রিটিশ আর ফরাসিদের জানায় বলে উল্লেখ করা হয়।
এরপর ব্রিটেনের Bletchley Park-এ Alan Turing, Gordon Welchman প্রমুখ “bombe” নামের যন্ত্র নকশা করেন, যা known-plaintext ধারণার ওপর চলত: সামরিক বার্তায় প্রায়ই আসে এমন শব্দ (crib) একটি নির্দিষ্ট অবস্থানে আছে ধরে, যন্ত্রটি সেই অনুমানের সঙ্গে অসংগত সেটিংগুলো দ্রুত বাদ দিত। “কোনো অক্ষর নিজের কাছে যায় না” গঠনটি এখানে সাহায্য করত, কারণ crib কোথায় বসতে পারে না তা আগেই বাদ দেওয়া যেত। এটি আমাদের key-পুনর্ব্যবহারের ব্লকের “SEND ” অনুমানের বড় রূপ।
দুটি শিক্ষা এখানে স্পষ্ট। প্রথমত, এই কাজ ছিল মূলত ডিসক্রিট ম্যাথ: permutation, চক্রের গঠন, গণনা আর সম্ভাবনা। দ্বিতীয়ত, যন্ত্রের গাণিতিক দুর্বলতার সঙ্গে ব্যবহারকারীর অভ্যাস (পুনরাবৃত্ত শব্দ, অনুমানযোগ্য বার্তার শুরু) মিলে আক্রমণ সম্ভব হয়েছিল; Kerckhoffs-এর নীতি অনুযায়ী যন্ত্রের নকশা জানা ছিল, গোপন ছিল শুধু দৈনিক সেটিং, আর তবু ভাঙা গেছে।
মূল উপলব্ধি: জটিল cipher-ও তার গাণিতিক গঠনের invariant আর ব্যবহারকারীর অনুমানযোগ্য অভ্যাস দিয়ে ভাঙা যায়; তাই আধুনিক নকশায় গঠনগত নিয়মিততা আর known-plaintext দুটোর বিরুদ্ধেই প্রতিরোধ চাওয়া হয়।
অনুশীলন
আগে খাতায় করুন, তারপর উত্তর খুলে মেলান।
-
প্রমাণ: E1(x) = a1x + b1 আর E2(x) = a2x + b2 (mod 26) দুটি বৈধ affine cipher (gcd(a1, 26) = gcd(a2, 26) = 1). প্রমাণ করুন, E2 ∘ E1-ও একটি বৈধ affine cipher, আর তার key বের করুন।
- E2(E1(x)) ≡ a2(a1x + b1) + b2 = (a2a1)x + (a2b1 + b2) (mod 26). তাই এটি affine, key (a2a1 mod 26, a2b1 + b2 mod 26). — বীজগণিত
- বৈধতার জন্য দেখাতে হবে gcd(a2a1, 26) = 1. ধরুন একটি মৌলিক p, 26 আর a2a1 দুটোকেই ভাগ করে। Euclid-এর lemma-য় p ∣ a1 বা p ∣ a2, তাহলে সেই a-এর সঙ্গে 26-এর gcd ≥ p > 1, যা অনুমানের বিরোধী। — Euclid-এর lemma, পর্ব ২০
- বিকল্প পথ: a1−1a2−1 হলো a2a1-এর inverse, আর inverse থাকা ⇔ gcd = 1 (পর্ব ২৩). ∎
পরিণাম: পরপর দুটি affine encryption নতুন কোনো নিরাপত্তা দেয় না, কারণ ফল ৩১২টি key-এর একটি (§৮-এর ভুল প্রমাণ দেখুন)। উদাহরণ (7, 3) ∘ (5, 8) = (9, 7), scratch-এ ২৬টি অক্ষরে যাচাই করা।
-
হিসাব: (ক) affine key (5, 8)-এ ciphertext অক্ষর U কোন plaintext অক্ষর থেকে এসেছে? (খ) একটি বর্ণমালায় ২৯টি চিহ্ন (mod 29) হলে affine key কয়টি? ২৭টি চিহ্নে (২৬ অক্ষর আর ফাঁকা জায়গা) কয়টি? (গ) Vigenère key DHAKA দিয়ে RICE encrypt করুন। (ঘ) SYLHET শব্দের index of coincidence কত?
(ক) U = 20. x = 21 · (20 − 8) = 252 = 9 · 26 + 18, তাই 18 = S. যাচাই: 5 · 18 + 8 = 98 = 3 · 26 + 20 = U ✓ (ব্লক ১-এর আউটপুটেও SEND-এর S থেকে U).
(খ) 29 মৌলিক, তাই φ(29) = 28, আর key 28 · 29 = 812. 27 = 33, তাই φ(27) = 27 · (2/3) = 18, আর key 18 · 27 = 486. মৌলিক আকারের বর্ণমালায় 0 ছাড়া সব a বৈধ।
(গ) D = 3, H = 7, A = 0, K = 10. R(17) + 3 = 20 = U, I(8) + 7 = 15 = P, C(2) + 0 = C, E(4) + 10 = 14 = O. ফল UPCO.
(ঘ) S, Y, L, H, E, T ছয়টি অক্ষরই আলাদা, তাই প্রতিটি ni(ni − 1) = 0, আর IC = 0/30 = 0. খুব ছোট লেখায় IC অর্থবহ নয়; সূত্রটি বড় লেখার পরিসংখ্যান। সব সংখ্যা scratch-এ Python দিয়ে যাচাই করা।
-
আউটপুট অনুমান: কোড না চালিয়ে বলুন কী ছাপা হবে। তারপর যেকোনো Python-এ চালিয়ে মেলান।
A = "ABCDEFGHIJKLMNOPQRSTUVWXYZ" print("".join(A[(3 * A.index(c) + 1) % 26] for c in "CAB")) print(sorted({(4 * x) % 26 for x in range(26)})[:5], pow(9, -1, 26)) print(len({(a, b) for a in range(26) for b in range(26) if a % 2 and a != 13}))HBE [0, 2, 4, 6, 8] 3 312প্রথম লাইন: affine (3, 1); C = 2 → 7 = H, A = 0 → 1 = B, B = 1 → 4 = E. দ্বিতীয় লাইন: gcd(4, 26) = 2, তাই 4x mod 26 শুধু জোড় মানগুলো নেয় (১৩টি), প্রথম পাঁচটি 0, 2, 4, 6, 8; আর 9 · 3 = 27 ≡ 1, তাই 9−1 = 3. তৃতীয় লাইন: বিজোড় আর 13 নয় এমন a ঠিক ১২টি, প্রতিটির সঙ্গে ২৬টি b, মোট 312, অর্থাৎ বৈধ affine key-এর সংখ্যা।
-
প্রমাণের ভুল ধরুন: একজন লিখলেন: “দাবি: a ≠ 0 হলেই x ↦ ax + b (mod 26) একটি বৈধ cipher. প্রমাণ: ধরুন ax + b ≡ ay + b. b বাদ দিলে ax ≡ ay. a ≠ 0, তাই দুই পাশ a দিয়ে ভাগ করে x ≡ y. অর্থাৎ ফাংশনটি one-to-one, আর সসীম সেটে one-to-one মানে bijection. ∎” ভুল কোথায়, আর একটি counterexample দিন।
- ভুল: “a ≠ 0, তাই a দিয়ে ভাগ” ধাপটি সাধারণ সংখ্যার নিয়ম, mod 26-এর নয়। Congruence-এ a কেটে দেওয়া বৈধ শুধু gcd(a, 26) = 1 হলে, কারণ তখনই a−1 আছে আর দুই পাশ a−1 দিয়ে গুণ করা যায় (পর্ব ২৩)। ℤ26-এ শূন্য নয় এমন দুটি সংখ্যার গুণফল শূন্য হতে পারে, যেমন 2 · 13 ≡ 0.
- Counterexample: a = 2, b = 5: x = 0 (A) আর y = 13 (N) দুটোই 5 = F দেয় (scratch-এ AN → FF), অথচ 0 ≢ 13. ফাংশনটি one-to-one নয়।
- সংশোধন: দাবিটি হবে “gcd(a, 26) = 1 ⇔ বৈধ” (উপপাদ্য ৩.২), আর প্রমাণে ভাগের জায়গায় a−1 দিয়ে গুণ।
নিজেকে যাচাই করুন
ভুল হলে কারণসহ ব্যাখ্যা পাবেন, আবার চেষ্টাও করতে পারবেন।
ক্রমশুধু ciphertext হাতে নিয়ে একটি Vigenère cipher ভাঙার ধাপগুলো সঠিক ক্রমে সাজান (▲▼ চেপে সরান)
- পুনরাবৃত্ত অংশের দূরত্ব বা কলামের index of coincidence দিয়ে key-এর দৈর্ঘ্য m অনুমান করা
- ciphertext-কে m-টি কলামে ভাগ করা (অবস্থান j, j + m, j + 2m, …)
- প্রতিটি কলামে অক্ষর গুনে বা χ² দিয়ে সেই কলামের shift বের করা
- m-টি shift একসঙ্গে বসিয়ে key-শব্দ গঠন করা
- পুরো লেখা decrypt করে অর্থবহ কি না যাচাই করা
26 অক্ষরের বর্ণমালায় affine cipher x ↦ ax + b (mod 26)-এর মোট বৈধ key (a, b) কয়টি?
- 676
- 26
- 312
- 650
7-এর modular inverse mod 26 কত?
- 19
- 15
- 4
- inverse নেই
একটি লম্বা ইংরেজি লেখা shift cipher দিয়ে encrypt করা হয়েছে, আর ciphertext-এ সবচেয়ে ঘন অক্ষর H. key k-এর সবচেয়ে সম্ভাব্য মান কোনটি (A = 0)?
- 7
- 23
- 4
- 3
একই one-time pad key k দিয়ে দুটি বার্তা m1, m2 encrypt করে c1 = m1 ⊕ k আর c2 = m2 ⊕ k পাঠানো হলো। শুধু c1 আর c2 দেখে আক্রমণকারী কী পান?
- m1 ⊕ m2, key ছাড়াই
- সরাসরি key k
- কিছুই না, one-time pad সবসময় perfectly secret
- শুধু বার্তা দুটির দৈর্ঘ্য
একটি Vigenère ciphertext-কে m = 1, 2, …, 8 কলামে ভাগ করে কলামগুলোর গড় index of coincidence পাওয়া গেল প্রায় 0.043, 0.049, 0.052, 0.047, 0.042, 0.066, 0.042, 0.048. key-এর দৈর্ঘ্য সবচেয়ে সম্ভবত কত?
- 3
- 6
- 1
- 8
Kerckhoffs-এর নীতি (১৮৮৩) কোনটি বলে?
- নিরাপত্তার জন্য encryption পদ্ধতি গোপন রাখতে হবে
- key অন্তত বার্তার সমান লম্বা হতে হবে
- পদ্ধতি প্রকাশ্য হলেও নিরাপত্তা থাকতে হবে; গোপন শুধু key
- key space এত বড় হতে হবে যে brute force অসম্ভব
শব্দকোষ
লেখার মধ্যে ডটেড দাগ দেওয়া শব্দে মাউস রাখুন বা চাপুন, অর্থ দেখাবে। এই পর্বের সব নতুন শব্দ এখানে একসাথে:
মনে রাখুন
- Cipher: প্রতিটি key-এ Ek একটি bijection আর Dk = Ek−1. Kerckhoffs: পদ্ধতি প্রকাশ্য, গোপন শুধু key.
- Shift আর affine: x ↦ x + k আর x ↦ ax + b (mod 26). Affine বৈধ ⇔ gcd(a, 26) = 1; decryption a−1(y − b); key 12 · 26 = 312. দুবার encrypt করলে আবার একই ধরনের একটি key.
- ভাঙা: ছোট key space-এ brute force; monoalphabetic cipher-এ অক্ষর-গণনা অক্ষত থাকে, তাই frequency analysis আর χ2; affine-এ দুটি অনুমিত জোড়া থেকে linear congruence.
- Vigenère: yi = xi + ki mod m. Kasiski-র দূরত্ব আর কলামের IC (ইংরেজি প্রায় 0.066, সমান বিভাজন 1/26 ≈ 0.0385) m বলে দেয়; তারপর প্রতিটি কলাম একটি shift.
- One-time pad: সমসম্ভাব্য, বার্তার সমান লম্বা, একবারের key-এ perfect secrecy; Shannon: |𝓚| ≥ |𝓜| লাগবেই। key পুনর্ব্যবহারে c1 ⊕ c2 = m1 ⊕ m2. বাস্তবে শুধু যাচাই করা library.
আরও পড়ুন · ABCL TECH-এ আপনার পরবর্তী পদক্ষেপ
- পর্ব ২৮ · RSA-এর গণিত — key, encryption আর কেন কাজ করেপরবর্তী পর্বpublic key, RSA-এর key তৈরি আর সঠিকতার প্রমাণ।
- পর্ব ২৩ · Linear congruence আর Chinese Remainder TheoremপুনরালোচনাModular inverse আর linear congruence, যা affine decryption আর known-plaintext আক্রমণের ভিত্তি।
- ডিসক্রিট ম্যাথমেটিক্স · কোর্স হোম৮৩টি পর্বপুরো কোর্সের মানচিত্র আর ১৪টি মডিউল।
- Cybersecurity · পর্ব ০৭ · Cryptography-র ভিত্তিবাস্তব ব্যবহারগোপনীয়তা, অখণ্ডতা, পরিচয় আর Kerckhoffs-এর নীতি বাস্তব সিস্টেমে।
- Cybersecurity · পর্ব ০৮ · Symmetric encryptionআধুনিক cipherAES, mode, nonce আর authenticated encryption নিরাপদে ব্যবহারের নিয়ম।
- Cybersecurity · পর্ব ১৪ · Cryptography-র সাধারণ ভুলসাবধানতানিজে cipher না বানানো, key management আর key পুনর্ব্যবহারের বিপদ।