সরাসরি পর্বে যান
পর্ব ২৯ · ৮৩-এর মধ্যে · মডিউল ৫

Discrete logarithm আর Diffie–Hellman key exchange

Discrete Logarithms and Diffie–Hellman — Cyclic Groups and Generators, the Discrete Log Problem, Key Exchange with Proof, Man-in-the-Middle, Baby-Step Giant-Step, Pohlig–Hellman, Safe Primes and ElGamal Encryption

Taught in Bangla, this lesson covers the mathematics of key exchange with toy numbers. It builds cyclic groups mod p with a proven generator test, explains why the discrete logarithm is easy one way and believed hard the other, and proves Diffie–Hellman correct while showing why man-in-the-middle needs authentication. In-page Python runs toy key exchange, times baby-step giant-step, splits a group with Pohlig–Hellman and runs ElGamal, ending with safe primes and worked exam problems.

এই পর্বে যা শিখবেন

  • mod p-এর গুণের group, cyclic group আর generator চেনা, আর generator-এর পরীক্ষা প্রমাণসহ
  • Discrete logarithm-এর সংজ্ঞা, ধর্ম, আর কেন এক দিক সহজ কিন্তু অন্য দিক কঠিন বলে বিশ্বাস করা হয়
  • Diffie–Hellman key exchange হাতে চালানো, তার সঠিকতার প্রমাণ, আর ইভা কী দেখে ও কী দেখে না
  • Man-in-the-middle সমস্যার ধারণা, আর কেন পরিচয় যাচাই ছাড়া key exchange অসম্পূর্ণ
  • Baby-step giant-step আর Pohlig–Hellman-এর algorithm ও প্রমাণ, আর তা থেকে ElGamal encryption আর নিরাপদ parameter বাছাইয়ের নিয়ম

১ · খোলা পথে একই গোপন চাবি — বাজারঘরের সমস্যা

কাল্পনিক অনলাইন দোকান বাজারঘরের ঢাকা অফিসে কাজ করেন রিনা, আর চট্টগ্রামের গুদামে সুমন। প্রতিদিন রিনা গুদামে কিছু গোপন অর্ডার কোড পাঠান। পর্ব ২৭ “Classical cipher — shift, affine আর frequency analysis”-এর মতো যেকোনো symmetric cipher-এ দুজনের হাতে একই গোপন key লাগে। সমস্যা হলো, দুজনের যোগাযোগের একমাত্র পথ ইন্টারনেট, আর ধরে নিতে হবে সেই পথে যা যায় তা সবাই পড়তে পারে। পথের এই শ্রোতার নাম দেওয়া যাক ইভা (ইংরেজিতে eavesdropper, অর্থাৎ আড়ি পেতে শোনা লোক)।

১৯৭০-এর দশকের আগে এর একটাই উত্তর জানা ছিল: বিশ্বস্ত বাহক দিয়ে key হাতে হাতে পাঠানো, অনেকটা সিলমোহর করা খামে চাবি পাঠানোর মতো। হাজার হাজার গ্রাহক আর সার্ভারের যুগে এটি অচল। প্রশ্নটি তাই এমন দাঁড়ায়: দুজন মানুষ, যারা আগে কখনো কিছু ভাগ করেননি, শুধু খোলা পথে কথা বলে কি একই গোপন সংখ্যায় পৌঁছাতে পারেন, যাতে সব কথা শুনেও ইভা সেই সংখ্যা না পান?

রঙ মেশানোর উপমা: রিনা আর সুমন প্রকাশ্যে একটি সাধারণ রঙ ঠিক করেন, ধরুন হলুদ। প্রত্যেকে নিজের একটি গোপন রঙ বাছেন (রিনা লাল, সুমন নীল), সেটি হলুদের সঙ্গে মেশান, আর মিশ্রণটি খোলা পথে পাঠান। এবার প্রত্যেকে অন্যের পাঠানো মিশ্রণে নিজের গোপন রঙ আবার মেশান। দুজনের হাতেই আসে হলুদ + লাল + নীল, অর্থাৎ একই রঙ। ইভা হলুদ আর দুটি মিশ্রণ দেখেছেন, কিন্তু মিশ্রণ থেকে রঙ আলাদা করা কঠিন বলে তিনি লাল বা নীল বের করতে পারেন না। গণিতে “মেশানো” হলো mod p-এ ঘাত তোলা, আর “আলাদা করা কঠিন” হলো discrete logarithm-এর কঠিনতা। উপমাটি একটি জায়গায় ভেঙে পড়ে: ইভা যদি দুটি মিশ্রণ একসঙ্গে মেশান, তিনি পান হলুদ + হলুদ + লাল + নীল, যা আসল রঙ নয়। গণিতেও এই ভুল ধারণাটি আমরা §৪-এ ধরব।

এই পর্বের মূল যন্ত্র একটি one-way function, অর্থাৎ এমন ফাংশন যা এক দিকে হিসাব করা সহজ কিন্তু উল্টো দিকে কঠিন বলে বিশ্বাস করা হয়। এখানে ফাংশনটি হলো k ↦ gk mod p. পর্ব ২২-এর repeated squaring (Modular arithmetic) দিয়ে এটি দ্রুত, আর পর্ব ২৪-এ primitive root আর discrete logarithm-এর যে নাম শুধু উল্লেখ করা হয়েছিল, এখানে তার পূর্ণ গণিত আসবে। Inverse আর CRT আসবে পর্ব ২৩ থেকে, আর √n ধাপের ভাবনা পর্ব ২৫ থেকে।

নিরাপত্তার সতর্কতা: এই পর্বের সব উদাহরণ (p = 23, p = 1000003 ইত্যাদি) শুধু গণিত বোঝার জন্য, আর এগুলো সম্পূর্ণ অনিরাপদ: একটি সাধারণ ল্যাপটপ এক সেকেন্ডের কম সময়ে এদের ভাঙে, যা আমরা নিজের হাতে দেখব। আসল ডেটা রক্ষায় কখনো নিজের লেখা Diffie–Hellman বা ElGamal ব্যবহার করবেন না। বাস্তব সিস্টেম যাচাই করা library, হাজার হাজার bit-এর parameter বা elliptic curve, পরিচয় যাচাইসহ key exchange আর authenticated encryption ব্যবহার করে। ব্যবহারিক দিক শিখুন Cybersecurity কোর্সের পর্ব ০৭ (cryptography-র ভিত্তি), পর্ব ১০ (public key আর key exchange) আর পর্ব ১৪ (সাধারণ ভুল) থেকে। এখানে সব “আক্রমণ” শুধু নিজের বানানো ছোট উদাহরণে, কেন parameter বড় হতে হয় তা দেখাতে।

২ · Cyclic group — mod p-এর গুণের চক্র

ঘড়ির কাঁটা বারো ঘর ঘুরে আবার শুরুতে ফেরে। mod p-এ একটি সংখ্যাকে বারবার নিজের সঙ্গে গুণ করলেও একই ঘটনা ঘটে: ঘাতগুলো একটি চক্রে ঘোরে। এই চক্রের ভাষা হলো group.

সংজ্ঞা ২.১ (group)

একটি সেট G আর তার ওপর একটি অপারেশন ∗ মিলে একটি group, যদি (১) বদ্ধতা: a, b ∈ G হলে a ∗ b ∈ G; (২) সংযোজন নিয়ম: (a ∗ b) ∗ c = a ∗ (b ∗ c); (৩) একক উপাদান e আছে, যাতে e ∗ a = a ∗ e = a; (৪) প্রতিটি a-এর inverse a−1 আছে, যাতে a ∗ a−1 = e. G-এর উপাদানের সংখ্যাকে group-এর order বলে।

মৌলিক p-এর জন্য লিখি ℤp* = {1, 2, …, p − 1}, অপারেশন গুণ mod p. এটি একটি group: দুটি সংখ্যা p দিয়ে বিভাজ্য না হলে Euclid-এর lemma অনুযায়ী তাদের গুণফলও বিভাজ্য নয় (বদ্ধতা); সাধারণ গুণের সংযোজন নিয়ম mod p-এও খাটে (পর্ব ২২); একক 1; আর gcd(a, p) = 1 বলে Bézout থেকে modular inverse আছে (পর্ব ২১). এর order p − 1 = φ(p). বিস্তারিত group তত্ত্ব আসবে পর্ব ৭০ “Group — symmetry, cyclic group আর Lagrange-এর উপপাদ্য”-এ; এখানে শুধু এই একটি group লাগবে।

সংজ্ঞা ২.২ (cyclic group আর generator)

একটি সসীম group G cyclic, যদি এমন g ∈ G থাকে যার ঘাতগুলো g0, g1, g2, … মিলে পুরো G. এমন g-কে G-এর generator বলে, আর লেখা হয় G = ⟨g⟩. ℤp*-এ generator মানে ঠিক পর্ব ২৪-এর primitive root: যে g-এর order ordp(g) = p − 1.

পর্ব ২৪-এর উপপাদ্য ৫.৩(ক) বলে প্রতিটি মৌলিক p-এ অন্তত একটি primitive root আছে (সেখানে প্রমাণ বাদ রাখা হয়েছিল, পাঠ্যবইয়ের সূত্রসহ)। তাই ℤp* সবসময় cyclic. নিচের ছবিতে p = 11, g = 2: প্রতিটি তীর মানে “2 দিয়ে গুণ করে mod 11”, আর দশ ধাপে সব দশটি ঘর ঘুরে 1-এ ফেরা।

2-এর ঘাত mod 11: একটি পূর্ণ চক্র

বৃত্তের ভেতরের সংখ্যা 2k mod 11, বাইরের কমলা লেখা ঘাত k. k থেকে মান পেতে শুধু তীর ধরে এগোনো; মান থেকে k পেতে পুরো বৃত্ত খুঁজতে হয়।

Powers of 2 modulo 11 form one full cycle through all ten nonzero residues 1 k=0 2 k=1 4 k=2 8 k=3 5 k=4 10 k=5 9 k=6 7 k=7 3 k=8 6 k=9 g = 2, p = 11 প্রতি তীরে ×2 সহজ: k → 2^k mod 11 কঠিন: 2^k mod 11 → k
উপপাদ্য ২.৩ (ঘাতের সমতা)

p মৌলিক আর g একটি generator mod p হলে, যেকোনো পূর্ণসংখ্যা i, j ≥ 0-এ gi ≡ gj (mod p) ⇔ i ≡ j (mod p − 1). ফলে k ↦ gk mod p ফাংশনটি {0, 1, …, p − 2} থেকে ℤp*-এ একটি bijection.

প্রমাণ:

  1. ধরুন i ≥ j. gi ≡ gj ⇔ gj(gi−j − 1) ≡ 0 ⇔ gi−j ≡ 1, কারণ gj-এর inverse আছে বলে তাকে কেটে দেওয়া যায়। — inverse দিয়ে গুণ, পর্ব ২৩
  2. পর্ব ২৪-এর উপপাদ্য ৫.২ অনুযায়ী gt ≡ 1 ⇔ ordp(g) ∣ t. এখানে ordp(g) = p − 1, তাই gi−j ≡ 1 ⇔ (p − 1) ∣ (i − j). — order-এর ধর্ম
  3. অর্থাৎ gi ≡ gj ⇔ i ≡ j (mod p − 1). — ধাপ ১–২
  4. বিশেষ করে 0 থেকে p − 2-এর p − 1টি ঘাত সব আলাদা মান দেয় (one-to-one). ℤp*-এরও ঠিক p − 1টি উপাদান, তাই সসীম সেটে one-to-one মানেই onto (পর্ব ১৬). ∎ — pigeonhole-এর যুক্তি

কোন g generator, তা চেনার জন্য সব ঘাত গোনার দরকার নেই। p − 1-এর মৌলিক উৎপাদক জানা থাকলে কয়েকটি ঘাতই যথেষ্ট।

উপপাদ্য ২.৪ (generator-এর পরীক্ষা)

p মৌলিক, 1 ≤ g ≤ p − 1. তাহলে g একটি generator mod p ⇔ p − 1-এর প্রতিটি মৌলিক উৎপাদক q-এর জন্য g(p−1)/q ≢ 1 (mod p).

প্রমাণ: d = ordp(g) লিখি; পর্ব ২৪ অনুযায়ী d ∣ p − 1.

  1. (⇒) g generator হলে d = p − 1. প্রতিটি q-এ (p − 1)/q < p − 1, আর d-এর চেয়ে ছোট কোনো ধনাত্মক ঘাতে gt ≡ 1 হতে পারে না। তাই g(p−1)/q ≢ 1. — order-এর সংজ্ঞা
  2. (⇐) contrapositive দেখাই: g generator না হলে কোনো q-তে শর্ত ভাঙে। g generator না হলে d একটি প্রকৃত ভাজক, d < p − 1, তাই (p − 1)/d > 1. — contrapositive, পর্ব ৮
  3. (p − 1)/d > 1-এর একটি মৌলিক উৎপাদক q নিন। তাহলে d ∣ (p − 1)/q, কারণ (p − 1)/q = d · ((p − 1)/d)/q আর ((p − 1)/d)/q একটি পূর্ণসংখ্যা। — বিভাজ্যতা
  4. d ∣ (p − 1)/q, তাই উপপাদ্য ৫.২ (পর্ব ২৪) অনুযায়ী g(p−1)/q ≡ 1, অর্থাৎ এই q-তে শর্ত ভাঙে। ∎ — order-এর ধর্ম

উদাহরণ: p = 23, p − 1 = 22 = 2 · 11. g = 5-এর জন্য 511 ≡ 22 ≡ −1 আর 52 ≡ 2, কোনোটিই 1 নয়, তাই 5 একটি generator. g = 2-এর জন্য 211 = 2048 = 23 · 89 + 1 ≡ 1, তাই 2 generator নয়; আসলে ord23(2) = 11. পর্ব ২৪-এর উপপাদ্য ৫.৩(গ) থেকে mod 23-এ generator ঠিক φ(22) = 10টি: 5, 7, 10, 11, 14, 15, 17, 19, 20, 21 (scratch-এ গুনে মেলানো)। বাস্তবে p − 1-এর উৎপাদক জানা না থাকলে এই পরীক্ষা চালানো যায় না; তাই নিরাপদ parameter এমনভাবে বানানো হয় যাতে p − 1-এর গঠন আগে থেকেই জানা থাকে (§৭-এর safe prime).

৩ · Discrete logarithm — সহজ দিক আর কঠিন দিক

সাধারণ লগারিদমে 10x = 1000 হলে x = log10 1000 = 3. mod p-এর জগতেও একই প্রশ্ন করা যায়, শুধু উত্তর একটি পূর্ণসংখ্যা আর হিসাব ভাগশেষে।

সংজ্ঞা ৩.১ (discrete logarithm)

p মৌলিক, g একটি generator mod p, আর h ∈ ℤp*. উপপাদ্য ২.৩ অনুযায়ী ঠিক একটি x ∈ {0, 1, …, p − 2} আছে, যাতে gx ≡ h (mod p). এই x-কে বলে g ভিত্তিতে h-এর discrete logarithm, লেখা হয় x = logg h. Discrete logarithm problem (DLP): p, g আর h দেওয়া থাকলে x বের করা।

ছবির চক্র থেকে mod 11-এ log2 9 = 6 (কারণ 26 = 64 = 55 + 9), log2 3 = 8, log2 10 = 5. সাধারণ লগারিদমের মতো এখানেও গুণ যোগে পরিণত হয়, শুধু ঘাতের হিসাব mod p − 1-এ।

উপপাদ্য ৩.২ (লগারিদমের নিয়ম)

g generator mod p, আর h1, h2 ∈ ℤp*, k ≥ 0. তাহলে logg(h1h2) ≡ logg h1 + logg h2 (mod p − 1), আর logg(hk) ≡ k · logg h (mod p − 1).

প্রমাণ:

  1. x1 = logg h1, x2 = logg h2 লিখি। তাহলে gx1 + x2 = gx1gx2 ≡ h1h2. — ঘাতের নিয়ম
  2. logg(h1h2)-এর সংজ্ঞায় glog(h1h2) ≡ h1h2. দুই ঘাত একই মান দেয়, তাই উপপাদ্য ২.৩-এ তারা mod p − 1-এ সমান। — উপপাদ্য ২.৩
  3. দ্বিতীয় নিয়ম: gk·x = (gx)k ≡ hk, আর একই যুক্তি। ∎ — ঘাতের নিয়ম

উদাহরণ mod 11: 9 · 3 = 27 ≡ 5. log 9 + log 3 = 6 + 8 = 14 ≡ 4 (mod 10), আর সত্যিই 24 = 16 ≡ 5 ✓. লক্ষ করুন, mod p নয়, mod p − 1: ঘাতের চক্রের দৈর্ঘ্য p − 1.

সহজ দিক: x থেকে gx

Repeated squaring (পর্ব ২২) দিয়ে gx mod p বের করতে x-এর bit-সংখ্যার প্রায় দ্বিগুণের বেশি গুণ লাগে না। x যদি 2048 bit-এর হয়, তাহলে বড়জোর প্রায় ৪,০০০ গুণ, আধুনিক যন্ত্রে এক সেকেন্ডের ক্ষুদ্র ভগ্নাংশ। Python-এ এটি একটি লাইন: pow(g, x, p). DSA কোর্সের পর্ব ৬২-এ এর কোড আর জটিলতা আছে।

কঠিন দিক: gx থেকে x

উল্টো দিকে সবচেয়ে সরল পথ হলো brute force: g0, g1, g2, … একটি একটি করে বানিয়ে h-এর সঙ্গে মেলানো, যাতে খারাপ ক্ষেত্রে p − 1টি গুণ লাগে। 2048 bit-এর p-তে সেটি প্রায় 22048 ধাপ, যা মহাবিশ্বের পরমাণুর আনুমানিক সংখ্যার চেয়েও বহু গুণ বেশি। আর সাধারণ লগারিদমের কৌশলও এখানে খাটে না। বাস্তব সংখ্যায় 2x x বাড়লে বাড়ে, তাই অনুমান করে ছোট-বড় দেখে বাইনারি সার্চ করা যায়। কিন্তু mod 11-এ 2-এর ঘাত 1, 2, 4, 8, 5, 10, 9, 7, 3, 6: মান লাফিয়ে বেড়ায়, কোনো ক্রম নেই, তাই “আরেকটু বড় x নিন” বলার উপায় নেই।

এর মানে এই নয় যে brute force-এর চেয়ে ভালো কিছু নেই। §৬-এ আমরা √(p − 1) মাপের একটি পদ্ধতি দেখব, আর ℤp*-এর জন্য আরও শক্তিশালী index calculus আর number field sieve পদ্ধতি আছে, যাদের সময় exponential-এর চেয়ে কম কিন্তু polynomial-এর চেয়ে বেশি; এদের বলে sub-exponential পদ্ধতি। তবু যথেষ্ট বড় আর সঠিকভাবে বাছা p-তে কোনো জানা algorithm-ই সাধারণ কম্পিউটারে যুক্তিসংগত সময়ে উত্তর দেয় না। গুরুত্বপূর্ণ কথা হলো, এটি একটি বিশ্বাস, প্রমাণ নয়। factoring-এর মতোই (পর্ব ২৫), DLP যে কঠিন তা কেউ প্রমাণ করেননি; প্রমাণ করতে পারলে P ≠ NP-এর চেয়েও শক্তিশালী একটি ফল প্রমাণ হতো।

কেন গুণ, যোগ নয়: একই প্রশ্ন যোগের group-এ (ℤn, +) করলে “ঘাত” হয়ে যায় গুণিতক: g-কে x বার যোগ করলে x·g mod n. সেখানে “log” বের করা মানে x·g ≡ h (mod n) সমাধান, যা পর্ব ২৩-এর inverse দিয়ে এক লাইনে হয়। অর্থাৎ কঠিনতা group-এর ওপর নির্ভর করে, শুধু “চক্র” থাকলেই হয় না। এ কারণেই cryptography এমন group খোঁজে যেখানে DLP কঠিন: ℤp*, আর পর্ব ৩০ “Finite field আর elliptic curve-এর পরিচয়”-এর elliptic curve-এর বিন্দুর group.

৪ · Diffie–Hellman key exchange — প্রোটোকল আর প্রমাণ

এবার রঙ মেশানোর উপমাটি গণিতে লেখা যাক। Diffie–Hellman একটি key exchange প্রোটোকল: দুই পক্ষ খোলা পথে কয়েকটি সংখ্যা বিনিময় করে একই গোপন সংখ্যায় পৌঁছান। এটি public-key cryptography-র প্রথম প্রকাশিত উদাহরণ।

প্রোটোকল ৪.১ (Diffie–Hellman)
  1. সর্বজনীন: একটি বড় মৌলিক p আর একটি g (generator, বা একটি বড় মৌলিক order-এর উপাদান), সবাই জানে।
  2. রিনা: গোপনে এলোমেলো a ∈ {2, …, p − 2} বাছেন, আর পাঠান A = ga mod p.
  3. সুমন: গোপনে এলোমেলো b বাছেন, আর পাঠান B = gb mod p.
  4. Key: রিনা হিসাব করেন K = Ba mod p, সুমন হিসাব করেন K′ = Ab mod p.
উপপাদ্য ৪.২ (সঠিকতা)

প্রোটোকল ৪.১-এ K = K′ = gab mod p.

প্রমাণ:

  1. B ≡ gb, আর congruence-এর দুই পাশকে একই ঘাতে তোলা যায় (পর্ব ২২-এর গুণের নিয়ম বারবার), তাই Ba ≡ (gb)a. — congruence-এ ঘাত
  2. (gb)a = gba = gab, পূর্ণসংখ্যার ঘাতের সাধারণ নিয়ম আর গুণের বিনিময় নিয়ম। — ঘাতের নিয়ম
  3. একইভাবে Ab ≡ (ga)b = gab. দুটিই gab-এর সঙ্গে congruent আর দুটিই 0 থেকে p − 1-এর মধ্যে, তাই K = K′. ∎ — transitivity

প্রমাণটি এত ছোট যে প্রশ্ন জাগে, এর জোর কোথায়? জোর গণিতে নয়, অসমতায়: রিনা আর সুমন প্রত্যেকে দুটি দ্রুত ঘাত তোলেন, কিন্তু ইভাকে a বা b বের করতে একটি discrete logarithm সমাধান করতে হয়। নিচের stepper ক্লাসিক ছোট উদাহরণটি হাতে করে দেখায়: p = 23, g = 5, a = 6, b = 15.

1সর্বজনীন: p = 23, g = 52রিনা: a = 6 (গোপন)3A = 5^6 mod 23 = 84সুমন: b = 15 (গোপন)5B = 5^15 mod 23 = 196ইভা দেখেন: 23, 5, 8, 197রিনা: K = 19^6 mod 23 = 28সুমন: K = 8^15 mod 23 = 2
চলক
এই ধাপের বর্গ
ফল
ইভা জানেন
  1. সর্বজনীন মান। p = 23 আর g = 5 সবাই জানে। §২-এ দেখেছি 52 ≡ 2 আর 511 ≡ 22, কোনোটিই 1 নয়, তাই 5 একটি generator.
  2. রিনার A. 52 = 25 ≡ 2, 54 ≡ 22 = 4. 6 = 4 + 2, তাই 56 = 54 · 52 ≡ 4 · 2 = 8. রিনা পাঠান A = 8.
  3. সুমনের B. 15 = 8 + 4 + 2 + 1, আর 58 ≡ 42 = 16. B ≡ 16 · 4 · 2 · 5 = 640 = 23 · 27 + 19, তাই B = 19.
  4. খোলা পথ। ইভা দেখেন 23, 5, 8 আর 19. গোপন a = 6 আর b = 15 কখনো পথে যায়নি।
  5. রিনার key. 192 = 361 ≡ 16, 194 ≡ 162 = 256 ≡ 3. তাই 196 = 194 · 192 ≡ 3 · 16 = 48 ≡ 2.
  6. সুমনের key. 82 = 64 ≡ 18, 84 ≡ 182 = 324 ≡ 2, 88 ≡ 4. 815 ≡ 4 · 2 · 18 · 8 = 1152 ≡ 2. দুজনেরই K = 2. যাচাই: ab = 90 ≡ 2 (mod 22), আর 52 ≡ 2 ✓.

ইভা কী জানেন আর কী জানেন না, তা টেবিলে:

মানরিনাসুমনইভা
p, gজানেনজানেনজানেন
A = ga, B = gbজানেনজানেনজানেন
aজানেননানা
bনাজানেননা
K = gabজানেনজানেননা (বলে বিশ্বাস)

ইভার আসল প্রশ্নকে বলে computational Diffie–Hellman (CDH) সমস্যা: g, ga আর gb জেনে gab বের করা। DLP সমাধান করতে পারলে CDH-ও হয়: A থেকে a বের করে Ba নিলেই হলো। উল্টোটা, অর্থাৎ CDH সমাধান করতে পারলে DLP-ও পারা যায় কি না, সাধারণভাবে প্রমাণিত নয়। তাই Diffie–Hellman-এর নিরাপত্তা আসলে CDH (এবং বাস্তবে আরও শক্ত একটি অনুমান) কঠিন হওয়ার বিশ্বাসের ওপর দাঁড়িয়ে।

একটি লোভনীয় ভুল ধারণা: “ইভা A আর B গুণ করলেই তো key পান।” A · B ≡ ga · gb = ga+b, যা gab নয়। ঘাতে যোগ আর গুণ গুলিয়ে ফেলা এই ভুলটিই রঙের উপমায় দুটি মিশ্রণ একসঙ্গে মেশানো। উপরের উদাহরণে A · B = 152 ≡ 14, অথচ K = 2. নিচের ব্লক একটু বড় মৌলিক p = 1000003-এ পুরো বিনিময় চালায়, generator নিজে খুঁজে নেয় (উপপাদ্য ২.৪ দিয়ে), আর stepper-এর সংখ্যাগুলো মেলায়। প্রথমবার চালালে ব্রাউজারে Python (Pyodide) নামতে কয়েক সেকেন্ড লাগে।

import random
random.seed(29)                       # fixed seed: same run every time
p = 1_000_003                         # prime; fine for learning, useless for security

def prime_factors(n):
    fs, d = [], 2
    while d * d <= n:
        if n % d == 0:
            fs.append(d)
            while n % d == 0: n //= d
        d += 1
    return fs + ([n] if n > 1 else [])

qs = prime_factors(p - 1)
g = next(c for c in range(2, p) if all(pow(c, (p - 1) // q, p) != 1 for q in qs))
print("primes of p-1:", qs, "| smallest generator g =", g)

a = random.randrange(2, p - 1)        # Rina's secret, never sent
b = random.randrange(2, p - 1)        # Sumon's secret, never sent
A, B = pow(g, a, p), pow(g, b, p)     # these travel over the open channel
print("Eve sees: p =", p, " g =", g, " A =", A, " B =", B)

key_r, key_s = pow(B, a, p), pow(A, b, p)
print("Rina's key =", key_r, " Sumon's key =", key_s, " equal:", key_r == key_s)
print("A*B mod p =", A * B % p, "(this is g^(a+b), not the key)")

mults = (a.bit_length() - 1) + (bin(a).count("1") - 1)
print(f"a = {a}: pow(g, a, p) needed only {mults} multiplications")
print("stepper (p=23, g=5, a=6, b=15):",
      pow(5, 6, 23), pow(5, 15, 23), pow(19, 6, 23), pow(8, 15, 23))

আসল আউটপুট (ব্রাউজারের Python):

primes of p-1: [2, 3, 166667] | smallest generator g = 2
Eve sees: p = 1000003  g = 2  A = 784466  B = 307332
Rina's key = 496790  Sumon's key = 496790  equal: True
A*B mod p = 781442 (this is g^(a+b), not the key)
a = 574746: pow(g, a, p) needed only 26 multiplications
stepper (p=23, g=5, a=6, b=15): 8 19 2 2

প্রথম লাইন উপপাদ্য ২.৪-এর কাজ: 1000002 = 2 · 3 · 166667, আর তিনটি ঘাত পরীক্ষা করেই 2-কে generator বলা গেছে। দুজনের key হুবহু এক (উপপাদ্য ৪.২), অথচ ইভার দেখা চারটি সংখ্যার কোনোটিই key নয়, আর A · B-ও নয়। শেষের আগের লাইন অসমতার সহজ দিক দেখায়: ছয় অঙ্কের ঘাত তুলতে মাত্র কয়েক ডজন গুণ। শেষ লাইন stepper-এর 8, 19, 2, 2 মেলায়। p এখানে এত ছোট যে §৬-এ আমরা নিজেরাই এক মুহূর্তে a বের করব।

ইতিহাস: Whitfield Diffie আর Martin Hellman ১৯৭৬ সালের নভেম্বরে IEEE Transactions on Information Theory-তে “New Directions in Cryptography” প্রবন্ধে এই পদ্ধতি প্রকাশ করেন। Ralph Merkle-এর প্রায় একই সময়ের “puzzle” ধারণা এর পূর্বসূরি, আর Hellman পরে পদ্ধতিটিকে Diffie–Hellman–Merkle নামে ডাকার প্রস্তাব দেন বলে উল্লেখ আছে। পরে প্রকাশিত তথ্য অনুযায়ী ব্রিটিশ গোয়েন্দা সংস্থা GCHQ-এর Malcolm Williamson এর কয়েক বছর আগে একই ধরনের পদ্ধতি পেয়েছিলেন, কিন্তু তা গোপন ছিল আর ১৯৯৭ সালে প্রকাশ্যে আসে। Diffie আর Hellman ২০১৫ সালের ACM Turing Award পান।

৫ · Man-in-the-middle — গণিত ঠিক, তবু বিপদ

এ পর্যন্ত ইভা শুধু শুনছিলেন। এবার ধরুন তিনি পথের মাঝখানে বসে বার্তা বদলাতেও পারেন, যেমন একটি আপোসকৃত Wi-Fi রাউটার বা মাঝপথের কোনো যন্ত্র। একে বলে man-in-the-middle (মাঝের লোক) পরিস্থিতি। এখানে কোনো গণিত ভাঙা হয় না; শুধু ধারণাটি বোঝা দরকার, কারণ এটিই দেখায় Diffie–Hellman একা কী নিশ্চয়তা দেয় না।

যা ঘটেফল
রিনা A = ga পাঠান; ইভা সেটি আটকে নিজের E = ge সুমনকে দেনসুমন ভাবেন E রিনার; সুমনের key gbe
সুমন B = gb পাঠান; ইভা সেটি আটকে নিজের E রিনাকে দেনরিনা ভাবেন E সুমনের; রিনার key gae
ইভা দুটি key-ই জানেন (Ae আর Be)রিনার বার্তা খুলে পড়ে আবার সুমনের key-তে বন্ধ করে পাঠাতে পারেন
রিনা আর সুমন দুজনেই প্রোটোকল ঠিকঠাক চালিয়েছেনকেউ কিছু টের পান না

উপপাদ্য ৪.২ ভুল হয়নি: রিনা সত্যিই যার সঙ্গে কথা বলছেন, তার সঙ্গে একই key পেয়েছেন। সমস্যা হলো সেই “কেউ” আসলে কে, তা Diffie–Hellman বলে না। গণিতের ভাষায়, প্রোটোকলটি গোপনীয়তা দেয় শুধু নিষ্ক্রিয় শ্রোতার বিরুদ্ধে; সক্রিয় প্রতিপক্ষের বিরুদ্ধে দরকার authentication, অর্থাৎ প্রমাণ যে A সত্যিই রিনার কাছ থেকে এসেছে।

বাস্তবে এটি করা হয় digital signature দিয়ে: সুমন নিজের দীর্ঘমেয়াদি গোপন key দিয়ে বিনিময়ের বার্তাগুলোয় সই করেন, আর রিনা আগে থেকে জানা বা certificate দিয়ে প্রত্যয়িত public key দিয়ে সই যাচাই করেন। ইভা E পাঠালে তাঁর কাছে সুমনের সই বানানোর key নেই, তাই প্রতারণা ধরা পড়ে। ওয়েবের HTTPS-এ TLS প্রোটোকল ঠিক এভাবে সার্ভারের certificate আর সই দিয়ে Diffie–Hellman-কে বাঁধে; Computer Networks কোর্সের পর্ব ৪৮ আর Cybersecurity কোর্সের পর্ব ১২-এ এর বিস্তারিত আছে। RSA-র সই কীভাবে কাজ করে, তার গণিত পর্ব ২৮ “RSA-এর গণিত — key, encryption আর কেন কাজ করে”-এ।

সই দিয়ে বাঁধা Diffie–Hellman-এর আরেকটি বড় সুবিধা আছে। প্রতিটি সংযোগে নতুন a আর b বানিয়ে কাজ শেষে মুছে ফেললে (ephemeral key), পরে কোনোদিন সার্ভারের দীর্ঘমেয়াদি সই-key চুরি হলেও আগের রেকর্ড করা আলাপ খোলা যায় না, কারণ সেই key দিয়ে শুধু সই হয়েছিল, a বা b বের হয় না। এই গুণের নাম forward secrecy, আর আধুনিক TLS 1.3 (RFC 8446, ২০১৮) certificate-ভিত্তিক সাধারণ সংযোগে শুধু এই ধরনের ephemeral key exchange রেখেছে; পুরোনো RSA key exchange সেখানে বাদ।

৬ · Brute force থেকে baby-step giant-step — √n-এর কৌশল

এখন ইভার দিক থেকে দেখা যাক, তবে শুধু আমাদের নিজের বানানো ছোট group-এ, কেন p বড় হতে হয় তা বুঝতে। ধরুন g-এর order n (ℤp*-এর generator হলে n = p − 1), আর h = gx, যেখানে 0 ≤ x < n. Brute force n পর্যন্ত গুণ নেয় আর প্রায় কোনো মেমরি নেয় না। Daniel Shanks-এর baby-step giant-step (সাধারণত ১৯৭১ সালের কাজ হিসেবে উল্লেখিত) মেমরির বিনিময়ে সময়কে √n-এ নামায়।

উপমা: একটি লম্বা রেললাইনে একটি হারানো মাইলফলক খুঁজছেন। প্রতিটি মিটার হাঁটার বদলে প্রথমে স্টেশনের কাছে ছোট ছোট পায়ে (baby step) m-টি চিহ্ন দেখে মনে রাখেন। তারপর লক্ষ্যের দিক থেকে বড় বড় লাফে (giant step, প্রতিটি m মিটার) পিছিয়ে আসেন, আর প্রতিবার দেখেন মনে রাখা কোনো চিহ্নে পৌঁছেছেন কি না। দুই ধরনের ধাপ মিলে প্রায় 2m, আর m ≈ √n হলে পুরো পথ ঢাকা পড়ে।

Algorithm ৬.১ (baby-step giant-step)
  1. m = ⌈√n⌉.
  2. Baby step: j = 0, 1, …, m − 1-এর জন্য gj mod p হিসাব করে একটি টেবিলে রাখুন (মান → j).
  3. c = g−m mod p বের করুন (inverse, পর্ব ২৩).
  4. Giant step: γ = h থেকে শুরু করে i = 0, 1, …, m − 1-এ: γ টেবিলে থাকলে (γ = gj) উত্তর x = im + j; না থাকলে γ ← γ · c.
উপপাদ্য ৬.২ (সঠিকতা আর খরচ)

g-এর order n, আর h = gx, 0 ≤ x < n. তাহলে Algorithm ৬.১ ঠিক x ফেরত দেয়, আর এতে বড়জোর m baby step, m giant step আর m মানের একটি টেবিল লাগে, যেখানে m = ⌈√n⌉.

প্রমাণ:

  1. Division algorithm-এ x = i0m + j0, যেখানে 0 ≤ j0 < m. যেহেতু x ≤ n − 1 ≤ m2 − 1, তাই i0 = ⌊x/m⌋ ≤ m − 1. — division algorithm, পর্ব ২০
  2. i-তম giant step-এ γ = h · g−im = gx − im. i = i0-এ γ = gj0, যা টেবিলে আছে। তাই algorithm বড়জোর i0 + 1 ≤ m giant step-এ থামে। — ঘাতের নিয়ম
  3. যে প্রথম i-তে থামে, সেখানে γ = gj, অর্থাৎ gx − im = gj, তাই gim + j = h: ফেরত দেওয়া y = im + j সত্যিই একটি লগারিদম। — ধাপ ২
  4. ফেরত দেওয়া y-ই x: ধাপ ৩-এ gy = gx, তাই y ≡ x (mod n) (উপপাদ্য ২.৩-এর যুক্তি, order n দিয়ে)। প্রথম থামা i ≤ i0. যদি i < i0 হয়, তবে 0 ≤ y ≤ (i + 1)m − 1 < i0m ≤ x < n, অর্থাৎ 0 < x − y < n অথচ n ∣ x − y, যা অসম্ভব। তাই i = i0, আর gj = gj0 যেখানে 0 ≤ j, j0 < m ≤ n, তাই j = j0 আর y = x. — contradiction আর উপপাদ্য ২.৩
  5. খরচ: টেবিল বানাতে m গুণ, c বের করতে একটি inverse আর একটি ঘাত, আর বড়জোর m giant step, প্রতিটিতে একটি গুণ আর একটি টেবিল-খোঁজ। ∎ — গণনা

নিচের stepper §৪-এর উদাহরণে ইভার ভূমিকা নেয়: p = 23, g = 5 (n = 22), আর সুমনের প্রকাশ্য মান h = 19. লক্ষ্য b = log5 19.

1n = 22, m = ⌈√22⌉ = 52baby: 5^j, j = 0..4 → 1, 5, 2, 10, 43c = 5^(−5) = 20^(−1) ≡ 154i = 0: γ = 19 ✗5i = 1: γ = 19·15 ≡ 9 ✗6i = 2: γ = 9·15 ≡ 20 ✗7i = 3: γ = 20·15 ≡ 1 ✓ (j = 0)8x = 3·5 + 0 = 15
চলক
i
γ
টেবিলে?
  1. মাপ ঠিক করা। g = 5-এর order n = 22. m = ⌈√22⌉ = 5, কারণ 42 = 16 < 22 ≤ 25 = 52. x = 5i + j আকারে লেখা যাবে, 0 ≤ i, j ≤ 4.
  2. Baby step. 50 = 1, 51 = 5, 52 = 25 ≡ 2, 53 ≡ 10, 54 ≡ 50 ≡ 4. এই পাঁচটি মান আর তাদের j টেবিলে রাখা হলো।
  3. Giant step-এর গুণক। 55 ≡ 4 · 5 = 20. তার inverse: 20 · 15 = 300 = 23 · 13 + 1, তাই c = 5−5 ≡ 15.
  4. i = 0. γ = h = 19. টেবিলে 19 নেই।
  5. i = 1. γ = 19 · 15 = 285 = 23 · 12 + 9, অর্থাৎ 9. টেবিলে নেই।
  6. i = 2. γ = 9 · 15 = 135 = 23 · 5 + 20, অর্থাৎ 20. টেবিলে নেই।
  7. i = 3, মিল। γ = 20 · 15 = 300 ≡ 1 = 50, তাই j = 0 আর x = 3 · 5 + 0 = 15. সুমনের গোপন b = 15 বেরিয়ে এলো, 22টি সম্ভাবনার বদলে 5টি baby আর 4টি giant step-এ।

এবার একটু বড় group: p = 1000003, g = 2, n = 1000002. নিচের ব্লক একটি গোপন x-এর লগারিদম brute force আর baby-step giant-step, দুই পদ্ধতিতে বের করে, ধাপ গোনে আর সময় মাপে।

import random, time
from math import isqrt
random.seed(6)
p, g = 1_000_003, 2                   # g = 2 generates all p - 1 residues
n = p - 1                             # the order of the group
secret = random.randrange(n)
h = pow(g, secret, p)                 # public: h = g^secret mod p

def brute(g, h, p):                   # try k = 0, 1, 2, ... in turn
    y = 1
    for k in range(p - 1):
        if y == h: return k, k + 1
        y = y * g % p

def bsgs(g, h, p, n):                 # Shanks: baby steps, then giant steps
    m = isqrt(n - 1) + 1              # m = ceil(sqrt(n))
    table, y = {}, 1
    for j in range(m):                # baby steps: store g^j -> j
        table.setdefault(y, j); y = y * g % p
    factor, gamma = pow(g, -m, p), h  # g^(-m) via the modular inverse
    for i in range(m):                # giant steps: h * g^(-i*m)
        if gamma in table: return i * m + table[gamma], m + i + 1
        gamma = gamma * factor % p

for name, solve in [("brute force", lambda: brute(g, h, p)),
                    ("baby-step giant-step", lambda: bsgs(g, h, p, n))]:
    t = time.perf_counter(); x, steps = solve(); t = time.perf_counter() - t
    print(f"{name:21s} x = {x}, steps = {steps:7d}, time = {t:.3f} s")
print("correct:", x == secret, "| sqrt(n) =", isqrt(n))
print("stepper (p=23, g=5, h=19):", bsgs(5, 19, 23, 22))

আসল আউটপুট (ব্রাউজারের Python; সময় আপনার যন্ত্রে আলাদা হবে):

brute force           x = 831877, steps =  831878, time = 0.048 s
baby-step giant-step  x = 831877, steps =    1833, time = 0.000 s
correct: True | sqrt(n) = 1000
stepper (p=23, g=5, h=19): (15, 9)

দুই পদ্ধতি একই x দিয়েছে। Brute force প্রায় আট লাখ গুণ করেছে, কারণ গোপন x-টি বড়; baby-step giant-step নিয়েছে দুই হাজারেরও কম ধাপ, যা 2√n ≈ 2000-এর মধ্যে, ঠিক উপপাদ্য ৬.২-এর মতো। শেষ লাইনে (15, 9): stepper-এর উত্তর 15, আর ধাপ 5 baby + 4 giant = 9. শিক্ষাটি হলো, ছয় অঙ্কের p এক মুহূর্তে ভাঙে। p-কে 2048 bit করলে √n প্রায় 21024, যা কোনো যন্ত্রের নাগালে নেই; তবে §৭-এ দেখবেন শুধু p-এর আকার যথেষ্ট নয়।

Baby-step giant-step-এর দুর্বলতা মেমরি: √n-টি মান মনে রাখতে হয়। John Pollard ১৯৭৮ সালে discrete log-এর জন্যও একটি rho পদ্ধতি দেন, যা পর্ব ২৫-এর factoring-এর rho-এর মতো চক্র খুঁজে প্রায় একই √n সময়ে কিন্তু খুব কম মেমরিতে কাজ করে (এটিও heuristic, প্রমাণিত সীমা নয়)। দুটিই যেকোনো group-এ চলে, তাই এদের বলা হয় “generic” পদ্ধতি।

৭ · Pohlig–Hellman, safe prime আর আসল আকার

√n-এর হিসাব থেকে মনে হতে পারে, p বড় হলেই নিরাপদ। কিন্তু Stephen Pohlig আর Martin Hellman ১৯৭৮ সালে দেখান, group-এর order n-এর গঠনও সমান গুরুত্বপূর্ণ। যদি n = p − 1 অনেকগুলো ছোট মৌলিকের গুণফল হয়, তাহলে DLP ছোট ছোট টুকরোয় ভেঙে যায়, আর Chinese Remainder Theorem (পর্ব ২৩) টুকরোগুলো জোড়া দেয়।

Lemma ৭.১ (subgroup-এ নামানো)

g-এর order n, h = gx, আর qe ∣ n (q মৌলিক)। k = n/qe নিয়ে g′ = gk আর h′ = hk লিখি। তাহলে (ক) g′-এর order ঠিক qe; (খ) g′x = h′, তাই x mod qe = logg′ h′.

প্রমাণ:

  1. (ক) যেকোনো t ≥ 0-এ g′t = gkt ≡ 1 ⇔ n ∣ kt (order-এর ধর্ম, পর্ব ২৪) ⇔ qek ∣ kt ⇔ qe ∣ t. তাই সবচেয়ে ছোট ধনাত্মক t হলো qe. — order-এর ধর্ম
  2. (খ) h′ = (gx)k = (gk)x = g′x. — ঘাতের নিয়ম
  3. g′-এর order qe, তাই উপপাদ্য ২.৩-এর যুক্তিতে g′x = g′y ⇔ x ≡ y (mod qe). অর্থাৎ order qe-এর ছোট group-এ লগারিদম বের করলে পাওয়া যায় ঠিক x mod qe. ∎ — উপপাদ্য ২.৩

Pohlig–Hellman পদ্ধতি তাই এরকম: n = q1e1 ⋯ qrer লিখে প্রতিটি qiei-এর ছোট group-এ baby-step giant-step চালানো, আর পাওয়া x mod qiei-গুলো CRT দিয়ে জুড়ে x mod n বের করা। খরচ মোটামুটি ∑ √(qiei), আর আরেকটু কৌশলে ∑ ei√qi. অর্থাৎ নিরাপত্তা ঠিক করে n-এর সবচেয়ে বড় মৌলিক উৎপাদক, n-এর আকার নয়। যে সংখ্যার সব মৌলিক উৎপাদক ছোট, তাকে smooth বলে, আর p − 1 smooth হলে DLP সহজ।

প্রতিরক্ষা তাই সরল: এমন p নিন যাতে p − 1-এ একটি বিশাল মৌলিক উৎপাদক থাকে। সবচেয়ে পরিচ্ছন্ন বাছাই safe prime: p = 2q + 1, যেখানে q-ও মৌলিক। তখন p − 1 = 2q, আর ℤp*-এর subgroup-গুলোর order শুধু 1, 2, q আর 2q. প্রায়ই g এমনভাবে বাছা হয় যাতে তার order ঠিক q (যেমন একটি বর্গসংখ্যা mod p), যাতে order-2-এর ছোট টুকরো থেকেও কিছু ফাঁস না হয়। মজার তথ্য: প্রোগ্রামিং প্রতিযোগিতার প্রিয় modulus 1000000007 নিজেই একটি safe prime (500000003-ও মৌলিক, scratch-এ যাচাই করা)।

নিচের ব্লক একই আকারের (প্রায় 109) দুটি মৌলিকে একই গোপন x খোঁজে: একটির p − 1 smooth, অন্যটি safe prime.

from math import isqrt
def factor(n):                        # trial division; n is small here
    f, d = {}, 2
    while d * d <= n:
        while n % d == 0: f[d] = f.get(d, 0) + 1; n //= d
        d += 1
    if n > 1: f[n] = f.get(n, 0) + 1
    return f

def bsgs(g, h, p, n):                 # g has order n; returns (x, steps)
    m = isqrt(n - 1) + 1; table, y = {}, 1
    for j in range(m): table.setdefault(y, j); y = y * g % p
    c, gam = pow(g, -m, p), h
    for i in range(m):
        if gam in table: return i * m + table[gam], m + i + 1
        gam = gam * c % p

def pohlig_hellman(g, h, p):
    n, x, M, steps = p - 1, 0, 1, 0
    for q, e in factor(n).items():
        qe = q ** e; k = n // qe      # jump into the subgroup of order q^e
        xi, s = bsgs(pow(g, k, p), pow(h, k, p), p, qe)
        steps += s
        t = (xi - x) * pow(M, -1, qe) % qe   # CRT: x mod M and xi mod qe
        x, M = x + M * t, M * qe
    return x, steps

secret = 123_456_789
for p, g in [(1_000_361_363, 2), (1_000_000_007, 5)]:
    h = pow(g, secret, p)
    x, steps = pohlig_hellman(g, h, p)
    print(f"p = {p}, p - 1 = {factor(p - 1)}")
    print(f"   x = {x}, correct: {pow(g, x, p) == h}, group steps: {steps}")
print("one BSGS on a group of order 10^9 needs up to", 2 * isqrt(10**9), "steps")

আসল আউটপুট (ব্রাউজারের Python):

p = 1000361363, p - 1 = {2: 1, 7: 2, 11: 1, 13: 2, 17: 2, 19: 1}
   x = 123456789, correct: True, group steps: 68
p = 1000000007, p - 1 = {2: 1, 500000003: 1}
   x = 123456789, correct: True, group steps: 27886
one BSGS on a group of order 10^9 needs up to 63244 steps

প্রথম মৌলিকে p − 1 = 2 · 72 · 11 · 132 · 172 · 19, সবচেয়ে বড় টুকরো 289. Pohlig–Hellman ছয়টি ছোট group-এ মাত্র ৬৮টি ধাপে x বের করেছে, যেখানে পুরো group-এ baby-step giant-step-এর প্রায় ৬৩ হাজার ধাপ লাগতে পারত। দ্বিতীয়টি safe prime: p − 1 = 2 · 500000003, তাই বড় টুকরোটি প্রায় পুরো group, আর Pohlig–Hellman কোনো সুবিধা পায়নি (প্রায় ২৮ হাজার ধাপ, সাধারণ baby-step giant-step-এর মাপেই)। একই আকারের দুটি p, অথচ একটি অন্যটির চেয়ে কয়েকশো গুণ দুর্বল।

বাস্তবের আকার — তারিখসহ, সতর্কভাবে

Generic পদ্ধতির বাইরে ℤp*-এর জন্য index calculus আর number field sieve-এর মতো sub-exponential পদ্ধতি আছে, তাই এখানে p-এর আকার √n-এর হিসাবের চেয়ে অনেক বড় রাখতে হয়। কয়েকটি প্রকাশিত, তারিখযুক্ত তথ্য (নিজে মূল উৎসে মিলিয়ে নিন, কারণ সুপারিশ সময়ের সঙ্গে বদলায়):

  • রেকর্ড: ২০১৯ সালের ডিসেম্বরে Boudot, Gaudry, Guillevic, Heninger, Thomé আর Zimmermann-এর দল একটি ২৪০ দশমিক অঙ্কের (প্রায় ৭৯৫ bit) safe prime-এ discrete logarithm সমাধানের ঘোষণা দেন, বিশাল কম্পিউটিং সময় খরচ করে।
  • Logjam (২০১৫): Adrian ও সহকর্মীদের গবেষণায় দেখা যায়, পুরোনো “export” মানের ৫১২ bit Diffie–Hellman ব্যবহারিকভাবে ভাঙা যায়, আর অনেক সার্ভার একই কয়েকটি p ভাগ করে ব্যবহার করত বলে একটি p-এর জন্য একবার বড় প্রস্তুতিমূলক হিসাব করলে বহু সংযোগে কাজে লাগে। তাঁদের অনুমান ছিল, ১০২৪ bit-ও বড় রাষ্ট্রীয় সম্পদের নাগালে থাকতে পারে।
  • সুপারিশ: NIST SP 800-57 Part 1 (Rev. 5, ২০২০)-এর টেবিলে finite-field Diffie–Hellman-এ ২০৪৮ bit p মোটামুটি ১১২ bit নিরাপত্তার সমান, আর ৩০৭২ bit প্রায় ১২৮ bit-এর। TLS-এর জন্য RFC 7919 (২০১৬) ffdhe2048 থেকে ffdhe8192 নামের নির্দিষ্ট safe-prime group দেয়।
  • Quantum: Peter Shor-এর ১৯৯৪ সালের algorithm factoring-এর মতো discrete log-ও polynomial সময়ে সমাধান করে, তবে তার জন্য বড় ও নির্ভুল quantum কম্পিউটার লাগে, যা এখনো তৈরি হয়নি। এ কারণে post-quantum পদ্ধতিতে সরে যাওয়ার কাজ চলছে; Cybersecurity কোর্সের পর্ব ১৪-এ, আর এই কোর্সের পর্ব ৩০-এর শেষে এর উল্লেখ আছে।

৮ · ElGamal encryption — Diffie–Hellman থেকে public-key encryption

Diffie–Hellman শুধু একটি key দেয়, বার্তা পাঠায় না। Taher ElGamal ১৯৮৫ সালে প্রকাশিত কাজে দেখান, একই ভাবনা দিয়ে সরাসরি public-key encryption বানানো যায়: গুদামের একটি স্থায়ী public key থাকে, আর যেকোনো কর্মী সেটি দিয়ে বার্তা বন্ধ করতে পারেন, কিন্তু খুলতে পারে শুধু গুদাম। ভাবনাটি হলো, প্রেরক প্রতিবার নিজে একটি নতুন Diffie–Hellman অর্ধেক বানান, আর পাওয়া key দিয়ে বার্তাকে গুণ করে ঢেকে দেন।

সংজ্ঞা ৮.১ (ElGamal encryption)
  1. Key তৈরি: সর্বজনীন p আর g. গুদাম গোপন x ∈ {2, …, p − 2} বাছে আর প্রকাশ করে h = gx mod p.
  2. Encrypt: বার্তা m ∈ ℤp*. প্রেরক প্রতিবার নতুন এলোমেলো k বাছেন, আর পাঠান c = (c1, c2) = (gk mod p, m · hk mod p).
  3. Decrypt: গুদাম s = c1x mod p বের করে, তারপর m = c2 · s−1 mod p.
উপপাদ্য ৮.২ (সঠিকতা)

সংজ্ঞা ৮.১-এর decryption সবসময় আসল m ফেরত দেয়।

প্রমাণ:

  1. s = c1x ≡ (gk)x = gkx = (gx)k ≡ hk. — ঘাতের নিয়ম, উপপাদ্য ৪.২-এর মতো
  2. hk ∈ ℤp*, অর্থাৎ p দিয়ে বিভাজ্য নয়, তাই s−1 আছে। — Euclid-এর lemma আর inverse
  3. c2 · s−1 ≡ m · hk · (hk)−1 ≡ m. আর 1 ≤ m ≤ p − 1, তাই ভাগশেষটি ঠিক m. ∎ — inverse-এর সংজ্ঞা

একটি ছোট সুবিধা: s−1 আলাদা করে বের না করে Fermat-এর ছোট উপপাদ্য দিয়ে সরাসরি c1p−1−x নেওয়া যায়, কারণ c1x · c1p−1−x = c1p−1 ≡ 1. নিচের stepper ধরে নেয় গুদামের গোপন x = 6 (তাই h = 56 ≡ 8, §৪-এর A), আর হাতে এসেছে ciphertext (17, 18).

1গোপন x = 6, c = (c₁, c₂) = (17, 18)217² ≡ 13, 17⁴ ≡ 13² ≡ 83s = 17⁶ = 17⁴·17² ≡ 8·13 ≡ 12412·2 = 24 ≡ 1 ⇒ s⁻¹ = 25m = 18·2 = 36 ≡ 136যাচাই: k = 7, 5⁷ ≡ 17, 13·8⁷ ≡ 18
চলক
c₁ = 17-এর ঘাত
s
s⁻¹
m
  1. শুরু। গুদাম জানে p = 23, g = 5 আর নিজের গোপন x = 6. হাতে এসেছে c1 = 17, c2 = 18. প্রেরকের k গুদাম জানে না, জানার দরকারও নেই।
  2. বর্গ। 172 = 289 = 23 · 12 + 13, তাই 172 ≡ 13. 174 ≡ 132 = 169 = 23 · 7 + 8, তাই 8.
  3. ভাগ করা গোপন মান। 6 = 4 + 2, তাই s = 176 ≡ 8 · 13 = 104 = 23 · 4 + 12, অর্থাৎ s = 12.
  4. Inverse. 12 · 2 = 24 ≡ 1 (mod 23), তাই s−1 = 2. (Fermat-এর পথে: 1722−6 = 1716 ≡ 2, একই উত্তর।)
  5. বার্তা। m = 18 · 2 = 36 ≡ 13. যাচাই: প্রেরক k = 7 নিয়েছিলেন; 57 ≡ 17 আর 87 ≡ 12, তাই c2 = 13 · 12 = 156 ≡ 18 ✓.

ElGamal-এর তিনটি বৈশিষ্ট্য মনে রাখার মতো। প্রথমত, এটি randomised: একই m দুবার encrypt করলে আলাদা k-এর কারণে আলাদা ciphertext আসে, তাই ইভা “এই দুটি বার্তা একই” বলতে পারেন না। এর দাম হলো ciphertext বার্তার দ্বিগুণ লম্বা। দ্বিতীয়ত, k-কে প্রতিবার নতুন আর অনুমান-অযোগ্য হতে হয়, অর্থাৎ CSPRNG থেকে আসতে হয় (পর্ব ২৬)। একই k দুবার দিলে দুটি c2-এর অনুপাত m2/m1, তাই একটি বার্তা জানা থাকলে অন্যটি x ছাড়াই বেরিয়ে আসে। তৃতীয়ত, textbook ElGamal malleable: c2-কে 2 দিয়ে গুণ করলে decrypt হয় 2m, অথচ কেউ টের পায় না। তাই বাস্তবে ElGamal-এর ভাবনাটি সরাসরি বার্তায় নয়, একটি key বানাতে ব্যবহৃত হয়, আর বার্তা যায় authenticated encryption দিয়ে; সাথে বার্তাকে একটি মৌলিক order-এর subgroup-এ বসানোর মতো সূক্ষ্ম শর্তও লাগে। নিচের ব্লক তিনটি বৈশিষ্ট্যই নিজের toy উদাহরণে দেখায়।

import random
random.seed(28)
p, g = 1_000_003, 2                   # toy group: NEVER for real data
x = random.randrange(2, p - 1)        # warehouse's private key
h = pow(g, x, p)                      # public key, published for everyone

def encrypt(m, h):
    k = random.randrange(2, p - 1)    # a fresh ephemeral k for every message
    return pow(g, k, p), m * pow(h, k, p) % p

def decrypt(c1, c2, x):
    s = pow(c1, x, p)                 # shared secret s = g^(xk)
    return c2 * pow(s, -1, p) % p     # m = c2 * s^(-1)

code = 4729                           # toy secret order code, m < p
ca, cb = encrypt(code, h), encrypt(code, h)
print("same message, two ciphertexts:", ca, cb)
print("both decrypt to:", decrypt(*ca, x), decrypt(*cb, x))

c1, c2 = ca                           # malleable: Enc(m) -> Enc(2m)
print("c2 doubled decrypts to:", decrypt(c1, 2 * c2 % p, x))

k = 777                               # mistake: the same k twice
m1, m2 = 1111, 4729                   # m1 is known (say, a public notice)
e1 = (pow(g, k, p), m1 * pow(h, k, p) % p)
e2 = (pow(g, k, p), m2 * pow(h, k, p) % p)
leak = e2[1] * pow(e1[1], -1, p) * m1 % p
print("same c1:", e1[0] == e2[0], "| m2 recovered without x:", leak)
print("stepper (p=23, x=6, c=(17,18)):",
      pow(17, 6, 23), pow(12, -1, 23), 18 * 2 % 23)

আসল আউটপুট (ব্রাউজারের Python):

same message, two ciphertexts: (931214, 279574) (800037, 398484)
both decrypt to: 4729 4729
c2 doubled decrypts to: 9458
same c1: True | m2 recovered without x: 4729
stepper (p=23, x=6, c=(17,18)): 12 2 13

একই কোড 4729 দুটি সম্পূর্ণ আলাদা ciphertext হয়েছে, আর দুটিই ঠিক 4729-এ ফেরে (উপপাদ্য ৮.২)। c2 দ্বিগুণ করায় decrypt হয়েছে 9458 = 2 · 4729: কেউ বার্তা না পড়েই বদলাতে পেরেছে। একই k দুবার দেওয়ায় দুটি c1 হুবহু এক, যা ইভার কাছে সংকেত; আর জানা m1 = 1111 থেকে গোপন key ছাড়াই m2 = 4729 বেরিয়ে এসেছে। শেষ লাইন stepper-এর s = 12, s−1 = 2 আর m = 13 মেলায়।

ElGamal-এর সই-পদ্ধতিও আছে (একই ১৯৮৫ সালের কাজে), আর তার একটি রূপান্তর থেকে NIST-এর Digital Signature Algorithm (DSA) এসেছে বলে সাধারণত বলা হয়।

৯ · পরীক্ষার অঙ্ক আর একটি ভুল প্রমাণ

বিশ্ববিদ্যালয়, চাকরি বা ইন্টারভিউর লিখিত পরীক্ষায় এই বিষয়ের যে ধাঁচের অঙ্ক আসে, তার চারটি পূর্ণ সমাধান। প্রতিটি সংখ্যা scratch-এ Python দিয়ে মেলানো।

অঙ্ক ১: p = 11, g = 2, a = 4, b = 7 হলে Diffie–Hellman-এর A, B আর K কত?

  1. A = 24 = 16 ≡ 5 (mod 11). B = 27 = 128 = 11 · 11 + 7 ≡ 7.
  2. রিনা: K = Ba = 74. 72 = 49 ≡ 5, তাই 74 ≡ 25 ≡ 3.
  3. সুমন: Ab = 57. 52 ≡ 3, 54 ≡ 9, 57 = 54 · 52 · 5 ≡ 9 · 3 · 5 = 135 ≡ 3. দুজনেরই K = 3. যাচাই: ab = 28 ≡ 8 (mod 10), 28 = 256 ≡ 3 ✓.

অঙ্ক ২: 3 আর 2 কি mod 17-এর generator?

  1. p − 1 = 16 = 24, মৌলিক উৎপাদক শুধু 2. উপপাদ্য ২.৪ অনুযায়ী শুধু g8 দেখলেই চলে।
  2. 32 = 9, 34 = 81 ≡ 13, 38 ≡ 169 ≡ 16 ≢ 1, তাই 3 generator.
  3. 28 = 256 = 17 · 15 + 1 ≡ 1, তাই 2 generator নয় (ord17(2) = 8). mod 17-এ generator মোট φ(16) = 8টি।

অঙ্ক ৩: baby-step giant-step দিয়ে 2x ≡ 9 (mod 11) সমাধান করুন

  1. n = 10, m = ⌈√10⌉ = 4. Baby step: 20..3 = 1, 2, 4, 8.
  2. c = 2−4: 24 = 16 ≡ 5, আর 5 · 9 = 45 ≡ 1, তাই c = 9.
  3. i = 0: γ = 9, টেবিলে নেই। i = 1: γ = 9 · 9 = 81 ≡ 4 = 22, তাই j = 2 আর x = 1 · 4 + 2 = 6. যাচাই: 26 = 64 ≡ 9 ✓.

অঙ্ক ৪: ElGamal, p = 11, g = 2, x = 3, বার্তা m = 7, k = 4

  1. Public key h = 23 = 8. c1 = 24 ≡ 5. hk = 84 = 4096 ≡ 4, তাই c2 = 7 · 4 = 28 ≡ 6. Ciphertext (5, 6).
  2. Decrypt: s = 53 = 125 ≡ 4 (encrypt-এর hk-এর সমান), s−1 = 3 (কারণ 4 · 3 = 12 ≡ 1).
  3. m = 6 · 3 = 18 ≡ 7 ✓.

প্রমাণের ভুল ধরুন: “শেয়ার করা key হলো A · B”

একজন লিখলেন: “দাবি: Diffie–Hellman-এ রিনা আর সুমনের key হলো A · B mod p. প্রমাণ: রিনার key Ba = (gb)a. ঘাতের নিয়মে (gb)a = gb · ga = ga+b. আবার A · B = gagb = ga+b. তাই key = A · B. ∎” পড়ে ভুলটি নিজে খুঁজুন।

ভুল কোথায়: দ্বিতীয় বাক্যের “ঘাতের নিয়ম” ভুল লেখা: (gb)a = gab, ga+b নয়। ga · gb = ga+b হলো গুণের নিয়ম, ঘাতের ঘাতের নয়। দাবিটি সত্য হলে ইভা শুধু শুনে key পেতেন, আর Diffie–Hellman অর্থহীন হতো। Counterexample: §৪-এর উদাহরণে A · B = 8 · 19 = 152 ≡ 14, অথচ key 2. ভুলটি ধরা পড়ে সবচেয়ে ছোট সংখ্যায় দুই দিক হিসাব করলেই।

আরও গভীরে: Victor Shoup ১৯৯৭ সালে প্রমাণ করেন, যে algorithm group-এর উপাদানকে শুধু “কালো বাক্স” হিসেবে দেখে (generic algorithm), তার DLP সমাধানে order-এর সবচেয়ে বড় মৌলিক উৎপাদক q-এর প্রায় √q ধাপ লাগবেই; অর্থাৎ baby-step giant-step আর Pollard rho এই শ্রেণিতে মূলত সর্বোত্তম। ℤp*-এ index calculus এই সীমা ভাঙে কারণ সে সংখ্যার গঠন (ছোট মৌলিকে ভাঙা) কাজে লাগায়; ভালো elliptic curve-এ এমন কোনো সুবিধা জানা নেই, তাই সেখানে অনেক ছোট key-তে একই নিরাপত্তা মেলে। আরেকটি সূক্ষ্ম অনুমান, decisional Diffie–Hellman (gab-কে এলোমেলো সংখ্যা থেকে আলাদা করা কঠিন), ElGamal-এর আধুনিক নিরাপত্তা-প্রমাণের ভিত্তি, আর পুরো ℤp*-এ এটি খাটে না বলেই মৌলিক order-এর subgroup লাগে।

পরীক্ষা ও ইন্টারভিউতে: বিশ্ববিদ্যালয়, চাকরি বা ইন্টারভিউর পরীক্ষায় এই ধরনের প্রশ্ন প্রায়ই আসে:

  • p = 23, g = 5, a = 6, b = 15-এ key? A = 8, B = 19, K = 2.
  • Generator চেনার দ্রুত উপায়? p − 1-এর প্রতিটি মৌলিক উৎপাদক q-এ g(p−1)/q ≢ 1.
  • Baby-step giant-step-এর খরচ? O(√n) সময় আর O(√n) মেমরি, m = ⌈√n⌉.
  • Safe prime কেন? p − 1 = 2q হলে Pohlig–Hellman কাজে আসে না; নিরাপত্তা ঠিক করে সবচেয়ে বড় মৌলিক উৎপাদক।
  • Diffie–Hellman কি man-in-the-middle ঠেকায়? না; সই বা certificate দিয়ে authentication লাগে।

১০ · AI-এর যুগে — discrete log আর key exchange-এর দাবি যাচাই

AI সহকারী Diffie–Hellman-এর ব্যাখ্যা চমৎকার দেয়, কিন্তু হিসাব আর শর্তে নিয়মিত কিছু ভুল করে। এগুলো চেনা থাকলে ধরতে এক মিনিট লাগে।

AI-এর ভুলকীভাবে ধরবেন
mod p-এর হিসাবে ভুল ভাগশেষ, বিশেষ করে বড় ঘাতে (যেমন 196 mod 23)প্রতিটি সংখ্যা pow(b, e, p) দিয়ে মিলিয়ে নিন; দুই পক্ষের key সমান কি না দেখুন
ঘাতকে mod p-এ কমানো (mod p − 1-এর বদলে), বা log-এর নিয়মে mod p লেখাউপপাদ্য ২.৩ মনে রাখুন: ঘাতের চক্রের দৈর্ঘ্য p − 1; ছোট p-এ দুই পথ হিসাব করে দেখুন
“g = 2 সবসময় generator” বা শুধু g(p−1)/2 দেখে generator বলাp − 1-এর সব মৌলিক উৎপাদক দিয়ে উপপাদ্য ২.৪ চালান; ছোট p-এ order গুনে দেখুন
“p বড় হলেই নিরাপদ”, p − 1-এর গঠন উপেক্ষাp − 1 factor করে সবচেয়ে বড় মৌলিক উৎপাদক দেখুন; Pohlig–Hellman মনে রাখুন
Diffie–Hellman “man-in-the-middle থেকে নিরাপদ” দাবি, বা textbook ElGamal-কে বাস্তবে ব্যবহারযোগ্য বলাAuthentication কোথায় হচ্ছে জিজ্ঞাসা করুন; বাস্তবে শুধু যাচাই করা library আর মান
বানানো রেকর্ড, ভুল সাল বা মানপত্রের ভুল নম্বরমূল প্রবন্ধ, RFC বা NIST নথিতে মিলিয়ে নিন; নিশ্চিত না হলে দাবি বাদ দিন

AI-এর কাছে এই ধরনের হিসাব বা প্রমাণ চাইলে এমন কাঠামো দিন, যাতে প্রতিটি দাবি আলাদা করে পরীক্ষা করা যায়:

নিচের discrete logarithm / Diffie–Hellman প্রশ্নটি ধাপে ধাপে সমাধান করো: প্রশ্ন: [যেমন একটি DH key, একটি generator পরীক্ষা, baby-step giant-step বা ElGamal] ১. কোন উপপাদ্য ব্যবহার করছ (generator-এর পরীক্ষা, Fermat, CRT, ইত্যাদি), তার পূর্ণ বিবৃতি আর সব শর্ত লেখো; p মৌলিক কি না আর g-এর order কত, তা আলাদা করে দেখাও। ২. প্রতিটি modular ঘাত repeated squaring-এ দেখাও: প্রতিটি বর্গ আর তার ভাগশেষ আলাদা লাইনে। ৩. ঘাত কমালে বলো mod p − 1 নাকি mod p, আর কেন। ৪. Diffie–Hellman হলে দুই পক্ষের key আলাদাভাবে হিসাব করে মিলিয়ে দেখাও। ৫. শেষে শুধু standard library-র pow() দিয়ে সব সংখ্যা যাচাইয়ের একটি ছোট Python কোড দাও। ৬. কোনো নিরাপত্তা-দাবি করলে সেটি শুধু ছোট toy উদাহরণের জন্য কি না স্পষ্ট বলো।

AI-এর উত্তর হাতে পেলে — যাচাইয়ের চেকলিস্ট

এই পর্বের প্রায় প্রতিটি দাবি p = 23-এর মতো ছোট মৌলিকে এক সেকেন্ডে brute force করা যায়; AI-এর “সবসময়” বা “কখনো না” দাবি পেলে আগে ছোট ক্ষেত্রে counterexample খুঁজুন।

ভাবনার প্রশ্ন

প্রতিটি প্রশ্ন নিয়ে নিজে কিছুক্ষণ ভাবুন, তারপর “→ উত্তর” চাপুন।

প্র ০১১৯৭৬ সালের Diffie–Hellman প্রবন্ধকে কেন cryptography-র মোড় ঘোরানো ঘটনা বলা হয়, যখন একই ধারণা গোপনে আগেই কারও কাছে ছিল?

১৯৭৬-এর আগে cryptography প্রায় পুরোপুরি symmetric ছিল: পাঠানো আর খোলার জন্য একই key, আর সেই key কোনো নিরাপদ পথে আগে থেকে ভাগ করতে হতো। সামরিক বাহিনী বা ব্যাংকের মতো প্রতিষ্ঠান বাহক, সিল করা ব্যাগ আর key-এর বই দিয়ে এই সমস্যা সামলাত। n জন মানুষ জোড়ায় জোড়ায় গোপনে কথা বলতে চাইলে প্রায় n2/2টি key লাগে, আর সবগুলো আগে থেকে বিলি করতে হয়; কম্পিউটার নেটওয়ার্কের যুগে এটি স্পষ্টতই অচল হয়ে উঠছিল।

Whitfield Diffie আর Martin Hellman তাঁদের “New Directions in Cryptography” প্রবন্ধে দুটি নতুন ধারণা প্রকাশ্যে আনেন। প্রথমটি এই পর্বের key exchange: খোলা পথে কথা বলেই গোপন key তৈরি। দ্বিতীয়টি আরও বড়: public-key cryptosystem আর digital signature-এর ধারণা, যেখানে encrypt করার key প্রকাশ্য আর খোলার key গোপন। তাঁরা তখন এমন একটি “trapdoor” ফাংশনের বাস্তব উদাহরণ দেননি; পরের বছর Rivest, Shamir আর Adleman RSA দিয়ে সেই শূন্যস্থান পূরণ করেন (পর্ব ২৮)। Ralph Merkle-এর “puzzle” ধারণাও একই দিকে ছিল, আর Hellman পরে তাঁর অবদান স্বীকার করে নামটিতে Merkle যোগ করার কথা বলেন বলে উল্লেখ আছে।

পরে প্রকাশিত তথ্য অনুযায়ী, যুক্তরাজ্যের GCHQ-তে James Ellis ১৯৭০ সালের দিকে “non-secret encryption”-এর ধারণা দেন, Clifford Cocks ১৯৭৩ সালে RSA-র মতো একটি পদ্ধতি পান, আর Malcolm Williamson এর পরপর Diffie–Hellman-এর মতো একটি পদ্ধতি পান। এই কাজ গোপন ছিল এবং ১৯৯৭ সালে প্রকাশ্যে আসে। তাহলে কৃতিত্ব কার? বিজ্ঞানের ইতিহাসে প্রভাব আসে প্রকাশ থেকে: গোপন আবিষ্কার কারও পরের কাজের ভিত্তি হতে পারেনি, অথচ ১৯৭৬-এর প্রবন্ধ প্রকাশ্য গবেষণার একটি পুরো শাখা খুলে দেয়, যার ফল আজকের HTTPS, মেসেজিং অ্যাপ আর ডিজিটাল সই। ২০১৫ সালের Turing Award এই প্রভাবেরই স্বীকৃতি।

মূল উপলব্ধি: একটি ধারণার মূল্য শুধু প্রথম কে ভেবেছিলেন তাতে নয়, কতজন সেটি পরীক্ষা, সমালোচনা আর ব্যবহার করতে পেরেছেন তাতেও; প্রকাশ্য গবেষণাই আধুনিক cryptography-কে বিশ্বাসযোগ্য করেছে।

প্র ০২Logjam গবেষণা দেখায়, অনেক সার্ভার একই কয়েকটি মৌলিক p ব্যবহার করত। একটি প্রকাশ্য p ভাগ করে নেওয়া কেন বিপজ্জনক হতে পারে, আর তবু মানক group কেন ব্যবহার করা হয়?

ℤp*-এর সবচেয়ে শক্তিশালী আক্রমণ, number field sieve, একটি বিশেষ গঠন মেনে চলে: হিসাবের বড় অংশ শুধু p-এর ওপর নির্ভর করে, নির্দিষ্ট h-এর ওপর নয়। এই “প্রস্তুতি” ধাপটি শেষ হলে সেই p-এর যেকোনো একক লগারিদম তুলনামূলক দ্রুত বের করা যায়। ২০১৫ সালে Adrian ও সহকর্মীদের Logjam গবেষণা এই বৈশিষ্ট্যকে বাস্তবের সঙ্গে মেলায়। তাঁরা দেখান, ১৯৯০-এর দশকের রপ্তানি-নিয়মের উত্তরাধিকার হিসেবে কিছু সার্ভার তখনো ৫১২ bit-এর “export” Diffie–Hellman গ্রহণ করত, আর প্রোটোকলের একটি দুর্বলতায় সংযোগকে সেই দুর্বল group-এ নামানো যেত। ৫১২ bit-এর জনপ্রিয় কয়েকটি p-এর প্রস্তুতি একবার শেষ করলে প্রতিটি সংযোগ মিনিটের মাপে ভাঙা যায় বলে তাঁরা দেখান।

আরও চিন্তার বিষয় ছিল ১০২৪ bit. তাঁদের অনুমান অনুযায়ী অল্প কয়েকটি ১০২৪ bit p অসংখ্য সার্ভারে ব্যবহৃত হচ্ছিল, আর একটি p-এর প্রস্তুতি সাধারণ গবেষকের নাগালের বাইরে হলেও বড় রাষ্ট্রীয় বাজেটের নাগালে থাকতে পারে। একবার সেই খরচ করা গেলে বিপুল পরিমাণ রেকর্ড করা ট্রাফিক খোলা যেত। এটি একটি অনুমান, প্রমাণিত ঘটনা নয়, কিন্তু এর যুক্তিটি গণিত থেকেই আসে: একই p ভাগ করা মানে একই প্রস্তুতি সবার বিরুদ্ধে কাজে লাগে।

তাহলে প্রত্যেকে নিজের p বানায় না কেন? কারণ নিজের p বানানোতেও ঝুঁকি: p সত্যিই মৌলিক কি না, p − 1-এ বড় মৌলিক উৎপাদক আছে কি না, g-এর order ঠিক কি না, সব ক্লায়েন্টকে যাচাই করতে হয়, আর ভুল বা ইচ্ছাকৃত দুর্বল p (§৭-এর smooth p − 1) সহজে ধরা পড়ে না। তাই আধুনিক মান উল্টো পথ নেয়: সুপরিচিত, প্রকাশ্যে বিশ্লেষিত safe-prime group (RFC 7919-এর ffdhe group) আর যথেষ্ট বড় আকার, যাতে প্রস্তুতি নিজেই অসাধ্য থাকে।

মূল উপলব্ধি: নিরাপত্তা শুধু একটি সংযোগের গণিত নয়, পুরো ইকোসিস্টেমের অর্থনীতিও; ভাগ করা parameter-এর আকার এমন হতে হয় যাতে “একবার খরচ, বহুবার লাভ” আক্রমণও অসম্ভব থাকে।

প্র ০৩DLP-এর কঠিনতা কেউ প্রমাণ করেননি। তবু এর ওপর পৃথিবীর বিশাল অংশের নিরাপদ যোগাযোগ দাঁড়িয়ে আছে। এই বিশ্বাসের ভিত্তি কী, আর এর সীমা কোথায়?

প্রথমে বুঝতে হবে আমরা কী জানি আর কী জানি না। DLP যদি সত্যিই কঠিন হয়, তবে P ≠ NP-ও সত্য হতে হবে, কারণ একটি pow দিয়েই DLP-এর সমাধান দ্রুত যাচাই করা যায়। P বনাম NP আজও খোলা প্রশ্ন, তাই DLP-এর কঠিনতার প্রমাণ এর চেয়েও বড় একটি অজানা প্রশ্নের পেছনে আটকে আছে। ফলে cryptography-র নিরাপত্তা-প্রমাণগুলো সাধারণত “যদি CDH কঠিন হয়, তবে এই প্রোটোকল নিরাপদ” ধাঁচের শর্তসাপেক্ষ বক্তব্য।

তাহলে বিশ্বাস আসে কোথা থেকে? তিনটি উৎস থেকে। প্রথমত, দীর্ঘ ইতিহাস: ১৯৭০-এর দশক থেকে বহু গবেষক চেষ্টা করেও সাধারণ কম্পিউটারে polynomial-সময়ের পদ্ধতি পাননি; উন্নতি এসেছে, কিন্তু sub-exponential সীমার ভেতরে, আর রেকর্ডগুলো (যেমন ২০১৯ সালের প্রায় ৭৯৫ bit) সুপারিশকৃত আকারের অনেক নিচে। দ্বিতীয়ত, আংশিক প্রমাণ: Victor Shoup ১৯৯৭ সালে দেখান যে generic algorithm-এর জন্য √q ধাপ লাগবেই, অর্থাৎ group-এর বিশেষ গঠন কাজে না লাগালে baby-step giant-step-এর চেয়ে মৌলিকভাবে ভালো করা অসম্ভব। তৃতীয়ত, সম্প্রদায়ের প্রকাশ্য বিশ্লেষণ: প্রস্তাবিত group আর প্রোটোকল বছরের পর বছর প্রকাশ্যে আক্রমণের মুখে থাকে।

সীমাও স্পষ্ট। Generic সীমা ℤp*-এ পুরোপুরি খাটে না, কারণ index calculus সংখ্যার গঠন কাজে লাগায়; তাই এখানে key অনেক বড় রাখতে হয়। কিছু বিশেষ ধরনের elliptic curve-এও অপ্রত্যাশিত দ্রুত আক্রমণ পাওয়া গেছে, তাই শুধু বিশ্লেষিত curve চলে। আর সবচেয়ে বড় সীমা: Shor-এর algorithm দেখায়, পর্যাপ্ত বড় quantum কম্পিউটারে DLP polynomial সময়ে সমাধানযোগ্য। এটি কোনো অনুমান নয়, একটি প্রমাণিত algorithm; অনিশ্চয়তা শুধু যন্ত্রটি কবে, আদৌ বানানো যাবে কি না। এই কারণেই “এখন রেকর্ড করো, পরে খোলো” ঝুঁকি মাথায় রেখে post-quantum পদ্ধতিতে সরে যাওয়ার প্রস্তুতি চলছে।

মূল উপলব্ধি: cryptography-র নিরাপত্তা প্রমাণিত সত্য নয়, যাচাই করা অনুমান; তাই শর্তগুলো জানা, parameter-এ নিরাপত্তার ব্যবধান রাখা আর অনুমান ভাঙলে দ্রুত বদলানোর প্রস্তুতি থাকা জরুরি।

প্র ০৪Man-in-the-middle ঠেকাতে সই লাগে, আর সই যাচাই করতে public key লাগে। কিন্তু সেই public key যে আসল, তা জানা যায় কীভাবে? এই চক্র কোথায় শেষ হয়?

প্রশ্নটি যথার্থ: Diffie–Hellman গোপনীয়তার সমস্যাকে পরিচয়ের সমস্যায় রূপান্তর করে, কিন্তু পরিচয়ের সমস্যা নিজে থেকে মেটায় না। সুমনের সই যাচাই করতে রিনার হাতে সুমনের public key থাকতে হবে, আর ইভা যদি সেই key-টিই বদলে দেন, তাহলে সব সুরক্ষা ভেঙে পড়ে। তাই কোথাও না কোথাও একটি বিশ্বাস আগে থেকে থাকতে হয়, যা খোলা পথে তৈরি করা যায় না।

বাস্তবে এর কয়েকটি সমাধান আছে। ওয়েবে certificate-ভিত্তিক PKI: ব্রাউজার আর অপারেটিং সিস্টেমে কয়েকশো certificate authority-র public key আগে থেকে বসানো থাকে, আর তারা সার্ভারের public key-তে সই করে certificate দেয়। TLS 1.3-এ সার্ভার পুরো handshake-এর একটি সারাংশে সই করে, যাতে Diffie–Hellman-এর বিনিময় আর সার্ভারের পরিচয় একসঙ্গে বাঁধা পড়ে। প্রোটোকল নকশায় এই “সই-করা Diffie–Hellman”-এর একটি পরিচিত পূর্বসূরি হলো Diffie, van Oorschot আর Wiener-এর ১৯৯২ সালের Station-to-Station প্রোটোকল। দ্বিতীয় সমাধান হলো সরাসরি যাচাই: মেসেজিং অ্যাপে দুজন key-এর একটি ছোট সংখ্যা বা QR কোড সামনাসামনি মিলিয়ে নেন। তৃতীয়টি “প্রথমবার বিশ্বাস”: SSH-এর মতো সিস্টেম প্রথম সংযোগে key মনে রাখে আর পরে বদলালে সতর্ক করে।

প্রতিটি সমাধানের দুর্বলতা আছে। Certificate authority আপোসকৃত হলে ভুয়া certificate তৈরি হতে পারে; এজন্য সব certificate প্রকাশ্য লগে লেখার (Certificate Transparency) মতো অতিরিক্ত ব্যবস্থা এসেছে। সরাসরি যাচাই নিরাপদ কিন্তু খুব কম মানুষ আসলে করেন। প্রথমবার বিশ্বাস করলে প্রথম সংযোগটিই দুর্বল। গণিত এখানে নিখুঁত হাতিয়ার দেয়, কিন্তু কাকে বিশ্বাস করবেন, তা শেষ পর্যন্ত মানুষ আর প্রতিষ্ঠানের সিদ্ধান্ত। বিস্তারিত Cybersecurity কোর্সের পর্ব ১০ আর ১২-এ।

মূল উপলব্ধি: key exchange গোপনীয়তা তৈরি করে, কিন্তু পরিচয় তৈরি করে না; প্রতিটি নিরাপদ ব্যবস্থার নিচে কোথাও একটি আগে থেকে স্থাপিত বিশ্বাস থাকে, আর নিরাপত্তা নির্ভর করে সেই বিশ্বাস কতটা সাবধানে রক্ষা করা হয়েছে তার ওপর।

প্র ০৫ElGamal-এ k একবার পুনরাবৃত্ত হলেই বার্তা ফাঁস হয়। এমন “একবার-ব্যবহারের” গোপন সংখ্যা বাস্তবে কেন বারবার সমস্যা তৈরি করে, আর প্রকৌশলীরা এখন কীভাবে এর ঝুঁকি কমান?

গণিতে নিয়মটি এক লাইনের: প্রতিটি বার্তায় নতুন, অনুমান-অযোগ্য k. §৮-এর ব্লক দেখিয়েছে, একই k দুবার এলে c2/c2′ = m/m′, তাই একটি বার্তা জানা থাকলে অন্যটি খুলে যায়। ElGamal-ধাঁচের সই-পদ্ধতিতে (DSA আর তার elliptic curve রূপ ECDSA) বিপদ আরও বড়: একই k দিয়ে দুটি ভিন্ন বার্তায় সই করলে দুটি linear সমীকরণ থেকে দীর্ঘমেয়াদি গোপন সই-key-ই বেরিয়ে আসে, যা পর্ব ২৩-এর inverse দিয়ে সমাধান করা যায়।

বাস্তবে এমন ঘটনার একটি বহুল আলোচিত উদাহরণ হলো ২০১০ সালের শেষের দিকে প্রকাশিত একটি জনপ্রিয় গেম কনসোলের সই-ব্যবস্থার দুর্বলতা, যেখানে গবেষকদের উপস্থাপনা অনুযায়ী প্রতিটি সইয়ে একই k ব্যবহৃত হচ্ছিল, ফলে নির্মাতার গোপন key বের করা সম্ভব হয়েছিল। আরেক ধরনের সমস্যা আসে দুর্বল random generator থেকে: পর্ব ২৬-এ দেখেছেন, LCG বা ভুলভাবে seed করা generator আসলে অনুমানযোগ্য, আর k সেখান থেকে এলে একই বিপদ।

প্রকৌশলীরা এখন ঝুঁকি কমান কয়েকভাবে। সইয়ের ক্ষেত্রে RFC 6979 (২০১৩) “deterministic nonce” দেয়: k গোপন key আর বার্তার hash থেকে একটি নির্দিষ্ট নিয়মে বানানো হয়, তাই random generator খারাপ হলেও একই k দুটি ভিন্ন বার্তায় আসে না। Ed25519-এর মতো আধুনিক সই-পদ্ধতিতে এই ভাবনা নকশার অংশ। Encryption-এ textbook ElGamal প্রায় ব্যবহৃত হয় না; তার বদলে key encapsulation আর authenticated encryption-এর মিশ্র নির্মাণ, যেখানে প্রতিটি অংশের ভুল-সহনশীলতা বিশ্লেষিত। আর সবচেয়ে বড় নিয়ম: এসব নিজে না লিখে যাচাই করা library ব্যবহার, যা Cybersecurity কোর্সের পর্ব ১৪-এর মূল কথা।

মূল উপলব্ধি: একটি নিয়ম যত ছোট, তত সহজে ভুলে যাওয়া যায়; ভালো নকশা ভুলকে অসম্ভব করে (deterministic nonce, পরীক্ষিত library), শুধু “সাবধান থাকুন” বলে না।

অনুশীলন

আগে খাতায় করুন, তারপর উত্তর খুলে মেলান।

  1. প্রমাণ: p মৌলিক আর g একটি generator mod p. প্রমাণ করুন, gk একটি generator ⇔ gcd(k, p − 1) = 1. এ থেকে দেখান যে mod p-এ generator ঠিক φ(p − 1)টি।
    1. d = gcd(k, p − 1) লিখি। (gk)t = gkt ≡ 1 ⇔ (p − 1) ∣ kt (উপপাদ্য ২.৩). — order-এর ধর্ম
    2. (p − 1) ∣ kt ⇔ ((p − 1)/d) ∣ (k/d)t ⇔ ((p − 1)/d) ∣ t, কারণ gcd((p − 1)/d, k/d) = 1 (পর্ব ২১). তাই ord(gk) = (p − 1)/d. — সহমৌলিক উৎপাদক বাদ দেওয়া
    3. gk generator ⇔ ord = p − 1 ⇔ d = 1. — সংজ্ঞা
    4. প্রতিটি উপাদান ঠিক একটি k ∈ {0, …, p − 2}-এর জন্য gk (উপপাদ্য ২.৩), তাই generator-এর সংখ্যা = ওই সীমায় p − 1-এর সঙ্গে সহমৌলিক k-এর সংখ্যা = φ(p − 1). ∎

    যাচাই: mod 23-এ φ(22) = 10টি generator, mod 29-এ φ(28) = 12টি, দুটিই scratch-এ গুনে মেলানো।

  2. হিসাব: p = 29, g = 2. (ক) উপপাদ্য ২.৪ দিয়ে দেখান 2 একটি generator. (খ) a = 5, b = 11 হলে Diffie–Hellman-এর A, B আর K কত? (গ) এই group-এ baby-step giant-step-এর m কত হবে?

    (ক) 28 = 22 · 7, মৌলিক উৎপাদক 2 আর 7. 228/2 = 214 ≡ 28 ≡ −1 ≢ 1, আর 228/7 = 24 = 16 ≢ 1. তাই 2 generator.

    (খ) A = 25 = 32 ≡ 3. B = 211 = 2048 = 29 · 70 + 18 ≡ 18. রিনা: 185; সুমন: 311. দুটিই ≡ 15, তাই K = 15. যাচাই: ab = 55 ≡ 27 (mod 28), আর 227 ≡ 15.

    (গ) n = 28, m = ⌈√28⌉ = 6, কারণ 52 = 25 < 28 ≤ 36. সব সংখ্যা scratch-এ Python দিয়ে যাচাই করা।

  3. আউটপুট অনুমান: কোড না চালিয়ে বলুন কী ছাপা হবে। তারপর যেকোনো Python-এ চালিয়ে মেলান।
    print([pow(3, k, 7) for k in range(6)])
    log = {pow(3, k, 7): k for k in range(6)}
    print(log[6], pow(3, -1, 7), (log[2] + log[4]) % 6)
    [1, 3, 2, 6, 4, 5]
    3 5 0

    3 একটি generator mod 7, তাই ছয়টি ঘাত 1–6 সবগুলো একবার করে দেয় (উপপাদ্য ২.৩)। log3 6 = 3, কারণ 33 = 27 ≡ 6. 3−1 ≡ 5, কারণ 3 · 5 = 15 ≡ 1. শেষ সংখ্যাটি উপপাদ্য ৩.২: তালিকায় 32 ≡ 2 আর 34 ≡ 4, তাই log 2 + log 4 = 6 ≡ 0 (mod 6). সত্যিই 2 · 4 = 8 ≡ 1 = 30, আর log 1 = 0 ✓. লক্ষ করুন, যোগফল mod 6 (অর্থাৎ p − 1), mod 7 নয়।

  4. প্রমাণের ভুল ধরুন: একজন লিখলেন: “দাবি: মৌলিক p-এ g(p−1)/2 ≢ 1 (mod p) হলেই g একটি generator. প্রমাণ: g generator না হলে তার order d একটি প্রকৃত ভাজক, তাই d ≤ (p − 1)/2, আর তখন g(p−1)/2 ≡ 1. ∎” ভুল কোথায়, আর একটি counterexample দিন।
    • ভুল: d ≤ (p − 1)/2 থেকে g(p−1)/2 ≡ 1 আসে না। দরকার d ∣ (p − 1)/2, শুধু ছোট হওয়া নয় (পর্ব ২৪-এর উপপাদ্য ৫.২). d যদি (p − 1)-এর এমন ভাজক হয় যা (p − 1)/2-কে ভাগ করে না, তবে এই পরীক্ষা ধরে না।
    • Counterexample: p = 7, g = 6. 63 = 216 ≡ 6 ≢ 1, অথচ 62 = 36 ≡ 1, অর্থাৎ ord7(6) = 2, generator নয়। এখানে d = 2, আর 2 ∤ 3.
    • সংশোধন: উপপাদ্য ২.৪: p − 1-এর প্রতিটি মৌলিক উৎপাদক q-এ g(p−1)/q ≢ 1 দেখতে হবে। p = 7-এ q = 3-এর পরীক্ষা 62 ≡ 1 ভুলটি ধরত। Safe prime-এ (p − 1 = 2q) শুধু দুটি পরীক্ষাই লাগে, এটিও তার একটি সুবিধা।

নিজেকে যাচাই করুন

ভুল হলে কারণসহ ব্যাখ্যা পাবেন, আবার চেষ্টাও করতে পারবেন।

ক্রমgx ≡ h (mod p) সমাধানে baby-step giant-step-এর ধাপগুলো সঠিক ক্রমে সাজান (▲▼ চেপে সরান)

  1. g-এর order n থেকে m = ⌈√n⌉ ঠিক করা
  2. j = 0, …, m − 1-এর gj হিসাব করে টেবিলে রাখা
  3. গুণক c = g−m mod p বের করা
  4. γ = h থেকে শুরু করে প্রতিবার γ টেবিলে আছে কি না দেখা, না থাকলে c দিয়ে গুণ
  5. মিল পেলে x = im + j লিখে gx ≡ h যাচাই করা

Diffie–Hellman-এ p = 23, g = 5, রিনার গোপন a = 3 আর সুমনের গোপন b = 7 হলে শেয়ার করা key কত?

  • 10
  • 9
  • 14
  • 21

মৌলিক p = 31-এ (p − 1 = 30 = 2 · 3 · 5) একটি g generator কি না জানতে উপপাদ্য অনুযায়ী কোন ঘাতগুলো পরীক্ষা করা যথেষ্ট?

  • শুধু g15
  • g15, g10 আর g6
  • শুধু g30
  • g1 থেকে g29 সব

একটি group-এ g-এর order n ≈ 1012. Baby-step giant-step-এ সময় আর মেমরি মোটামুটি কত?

  • প্রায় 106 মান মেমরিতে, প্রায় 2 · 106 গুণ
  • প্রায় 1012 গুণ, কোনো মেমরি নয়
  • প্রায় 40টি গুণ
  • প্রায় 1012 মান মেমরিতে, প্রায় 106 গুণ

প্রায় একই আকারের চারটি মৌলিক p-এর মধ্যে কোনটিতে ℤp*-এর discrete logarithm Pohlig–Hellman-এর বিরুদ্ধে সবচেয়ে শক্ত?

  • যার p − 1 একটি 2-এর ঘাত
  • যার p − 1-এর সব মৌলিক উৎপাদক 100-এর কম
  • যার p − 1-এর সবচেয়ে বড় মৌলিক উৎপাদক প্রায় √p
  • যা একটি safe prime, p = 2q + 1 (q মৌলিক)

ElGamal-এ (p = 23) একই k দিয়ে দুটি বার্তা encrypt হয়েছে: প্রথমটির c2 = 13 আর তার বার্তা m1 = 3 জানা; দ্বিতীয়টির c2′ = 18. গোপন key ছাড়াই দ্বিতীয় বার্তা কত?

  • 18
  • 13
  • 5
  • গোপন key ছাড়া বের করা অসম্ভব

মাঝপথে বসে বার্তা বদলাতে পারে এমন প্রতিপক্ষের (man-in-the-middle) বিরুদ্ধে Diffie–Hellman-কে নিরাপদ করতে কী লাগে?

  • আরও বড় মৌলিক p
  • g গোপন রাখা
  • বিনিময়ের মানে সই বা certificate দিয়ে পরিচয় যাচাই
  • Key দুবার হিসাব করে মেলানো

শব্দকোষ

লেখার মধ্যে ডটেড দাগ দেওয়া শব্দে মাউস রাখুন বা চাপুন, অর্থ দেখাবে। এই পর্বের সব নতুন শব্দ এখানে একসাথে:

মনে রাখুন

  1. Cyclic group: ℤp*-এর order p − 1; g generator ⇔ প্রতিটি মৌলিক q ∣ p − 1-এ g(p−1)/q ≢ 1. Generator ঠিক φ(p − 1)টি, আর gi ≡ gj ⇔ i ≡ j (mod p − 1).
  2. DLP: gx ≡ h থেকে x. সহজ দিক O(log x) গুণ, কঠিন দিকের কোনো দ্রুত পদ্ধতি জানা নেই, কিন্তু কঠিনতা প্রমাণিত নয়। log-এর হিসাব mod p − 1-এ।
  3. Diffie–Hellman: A = ga, B = gb, K = Ba = Ab = gab (A · B নয়)। p = 23, g = 5, a = 6, b = 15-এ K = 2. Man-in-the-middle ঠেকাতে সই বা certificate লাগে।
  4. আক্রমণ: baby-step giant-step: m = ⌈√n⌉, x = im + j, O(√n) সময় আর মেমরি। Pohlig–Hellman: CRT দিয়ে p − 1-এর মৌলিক টুকরোয় ভাঙা, তাই safe prime p = 2q + 1 আর বড় p.
  5. ElGamal: (c1, c2) = (gk, m·hk), m = c2·(c1x)−1. প্রতিবার নতুন k; textbook রূপ malleable আর শুধু শেখার জন্য।

পরের পর্বে: পর্ব ৩০ “Finite field আর elliptic curve-এর পরিচয়”: ℤp কেন একটি field, AES-এর GF(28)-এর গুণ, আর elliptic curve-এর বিন্দুর group, যেখানে একই Diffie–Hellman অনেক ছোট key-তে চলে।

আরও পড়ুন · ABCL TECH-এ আপনার পরবর্তী পদক্ষেপ

এই পর্বটি শেয়ার করুন:
এই পর্বে ভুল পেয়েছেন? জানান