Foundationপ্রথম নীতি থেকে
LEVEL 10লেসন ২/২০মাঝারি৪০ মিনিট

ক্রিপ্টোগ্রাফির ভিত্তি — চারটা লক্ষ্য, একটা নীতি

Cryptography Fundamentals — Four Goals, One Principle

চারটা security goal — confidentiality, integrity, authenticity, non-repudiation — চেনা, আর Kerckhoffs's Principle মেনে বোঝা যে নিরাপত্তা algorithm-এর গোপনীয়তায় নয়, চাবিতে থাকে; এটাই এই module-এর পুরো crypto toolbox-এর ভিত্তি।

এই লেসন শেষে আপনি পারবেন

  • Confidentiality, integrity, authenticity, আর non-repudiation — চারটা security goal-এর সংজ্ঞা আলাদা করে বলতে পারবেন, আর প্রতিটার জন্য একটা বাস্তব উদাহরণ দিতে পারবেন যেখানে শুধু সেই একটা goal-ই ভঙ্গ হয়
  • Kerckhoffs's Principle-এর মূল দাবি (নিরাপত্তা শুধু key-এর গোপনীয়তার ওপর নির্ভর করবে, algorithm-এর ওপর নয়) ব্যাখ্যা করে security-through-obscurity কেন ব্যর্থ হয় তার যুক্তি দিতে পারবেন
  • একটা obscurity-নির্ভর cipher-কে কোড চালিয়ে frequency analysis দিয়ে ভাঙতে পারবেন, আর সেটাকে একটা key-নির্ভর scheme-এর সাথে তুলনা করে পার্থক্যটা সংখ্যায় দেখাতে পারবেন
  • কেন production system-এ নিজের crypto primitive বানানো এড়ানো উচিত, অথচ এই module-এই toy hash/AES/RSA/DH বানানো হবে কেন যুক্তিসঙ্গত তা ব্যাখ্যা করতে পারবেন
  • hash function থেকে TLS পর্যন্ত এই module-এর crypto toolbox-এর roadmap সাজিয়ে বলতে পারবেন, প্রতিটা ধাপ আগেরটার ওপর কীভাবে দাঁড়ায়

আগে যা বোঝা থাকা দরকার

আগে এটা বুঝি

আগের লেসনে STRIDE দিয়ে threat model বানিয়েছিলাম — Spoofing, Tampering, Repudiation, Information Disclosure, Denial of Service, Elevation of Privilege। সেই মডেল একটা প্রশ্নের উত্তর দেয়: কী কী ভুল হতে পারে। কিন্তু একটা threat তালিকাভুক্ত করা মানেই সেটা ঠেকানো নয় — এখন লাগে আসল যন্ত্র।

এই module-এর বাকি লেসনগুলো ঠিক সেই যন্ত্রগুলোর দোকান: hash function, symmetric encryption, asymmetric encryption, digital signature, শেষে TLS — এই সবগুলোই আসলে একটাই ছোট সেট মৌলিক ধারণার ওপর দাঁড়িয়ে আছে। আজকের লেসন সেই ভিত্তিটা ঢালাই করবে — কোনো একটা নির্দিষ্ট algorithm-এর গভীরে না গিয়ে, বরং দুইটা প্রশ্নের উত্তর দিয়ে যেগুলো প্রতিটা পরের লেসনে ফিরে ফিরে আসবে:

প্রশ্ন ১ — "নিরাপত্তা" বলতে ঠিক কী বোঝায়? (একটা শব্দ না, চারটা আলাদা গ্যারান্টি)
প্রশ্ন ২ — একটা crypto system-এর নিরাপত্তা আসলে কীসের ওপর নির্ভর করে? (algorithm না, চাবি)

দ্বিতীয় প্রশ্নের উত্তরটা ১৮৮৩ সালে এক ডাচ ভাষাতাত্ত্বিক-cryptographer দিয়ে গিয়েছেন — Auguste Kerckhoffs — আর আজও প্রতিটা আধুনিক cryptosystem-এর ডিজাইন-নীতি এইখান থেকেই শুরু। আজকের লেসন শেষে এই দুইটা প্রশ্নের উত্তর এতটাই পাকা হয়ে যাবে যে পরের প্রতিটা লেসনে — hash function থেকে TLS পর্যন্ত — এই একই দুইটা প্রশ্নই বারবার জিজ্ঞেস করবেন, শুধু উত্তরটা প্রতিবার একটু বদলাবে।

মূল ধারণা

চারটা security goal — একটা শব্দ যথেষ্ট না

দৈনন্দিন কথায় “secure” শব্দটা একটা ধোঁয়াটে ছাতা — কিন্তু cryptography-তে এটাকে চারটা আলাদা, স্বাধীনভাবে-যাচাইযোগ্য গ্যারান্টিতে ভাঙা হয়। গুরুত্বপূর্ণ কথাটা হলো: এই চারটা স্বাধীন axis — একটা রক্ষা পেলে বাকি তিনটা এমনিতেই রক্ষা পায় না। নিচের টেবিলের তৃতীয় কলামে খেয়াল করুন — প্রতিটা উদাহরণে শুধু একটা গ্যারান্টিই ভাঙছে, বাকি তিনটা অক্ষত।

Goalসংজ্ঞাউদাহরণ — শুধু এই একটাই ভঙ্গ
Confidentialityশুধু অনুমোদিত পক্ষ content পড়তে পারবেকেউ wiretap করে message পড়ে ফেলল, কিন্তু কিছু বদলাল না, কাউকে জানালও না — content অবিকৃত পৌঁছাল, sender-ও আসল, শুধু গোপনীয়তা ভাঙল
Integrityডেটা unauthorized-ভাবে বদলায়নি, বদলালে ধরা পড়বেকেউ bank-transfer-এর amount ১০০ থেকে ১০০০ বদলে দিল — সে content পড়তে পারেনি (তাই confidentiality প্রাসঙ্গিকই না, encrypted ছিল), sender-ও আসল ছিল, শুধু মাঝপথে content বদলে গেল
AuthenticitySender সত্যিই দাবি করা ব্যক্তি/system-ই — impersonation নেইPhishing email — content নিখুঁতভাবে পড়া যাচ্ছে (confidentiality-র প্রশ্নই নেই, public message), পথে বদলায়নি (integrity ঠিক) — কিন্তু sender আসলে bank নয়, bank সেজে পাঠানো কেউ
Non-repudiationSender পরে অস্বীকার করতে পারবে না যে সে পাঠিয়েছিলএকজন ব্যবহারকারী সত্যিই একটা document sign করলেন (authenticity প্রমাণিত, content অবিকৃত — integrity ঠিক), কিন্তু system-এ cryptographic প্রমাণ না থাকায় পরে তিনি “আমি করিনি” বললে বিতর্ক মেটে না

লক্ষ্য করার মতো: integrity আর authenticity কাছাকাছি শোনায় কিন্তু আলাদা প্রশ্নের উত্তর দেয় — integrity বলে “content বদলায়নি”, authenticity বলে “sender-টা আসল”। দুইটাই সত্যি হলেও তৃতীয় একটা প্রশ্ন থেকে যায় — non-repudiation: sender পরে অস্বীকার করতে পারবে কি না। ডিজিটাল স্বাক্ষরের লেসনে দেখব কীভাবে একটাই primitive (asymmetric signature) এই তিনটাকেই একসাথে দেয়, যেখানে plain hash শুধু integrity দেয়, plain encryption শুধু confidentiality।

CIA Triad-এর সাথে সম্পর্ক

Security-এর সবচেয়ে পুরনো, সবচেয়ে বেশি উদ্ধৃত সংক্ষিপ্তসার হলো CIA Triad — Confidentiality, Integrity, Availability। খেয়াল করুন: এখানে কিন্তু তিনটা, উপরের টেবিলের চারটা না — triad-এ authenticity বা non-repudiation নেই, বদলে আছে availability (সঠিক সময়ে সিস্টেম ব্যবহারযোগ্য থাকা)।

ভেতরে কী ঘটছে

Kerckhoffs’s Principle — চাবিতেই নিরাপত্তা, algorithm-এ না

১৮৮৩ সালে ফরাসি সামরিক journal-এ Auguste Kerckhoffs “La Cryptographie Militaire” শিরোনামে একটা প্রবন্ধ লেখেন, যেখানে তিনি একটা ভালো সামরিক cipher-এর ছয়টা শর্ত (desiderata) লেখেন। দ্বিতীয় শর্তটাই ইতিহাসে টিকে গেছে, আজ যাকে বলা হয় Kerckhoffs’s Principle:

"Il faut qu'il n'exige pas le secret, et qu'il puisse sans
inconvénient tomber entre les mains de l'ennemi"

— system-টা শত্রুর হাতে সম্পূর্ণভাবে পড়ে গেলেও কোনো অসুবিধা হওয়া উচিত না,
   একমাত্র জিনিস যা গোপন থাকা লাগবে সেটা হলো KEY

মানে: ধরেই নিন adversary পুরো algorithm, পুরো protocol spec, এমনকি পুরো source code জানে — তারপরেও system নিরাপদ থাকতে হবে, শুধু একটা জিনিস তার কাছে না থাকলেই — key। ১৯৪৯ সালে Claude Shannon একই ধারণা আধুনিক তথ্যতাত্ত্বিক ভাষায় পুনর্গঠন করেন — যাকে বলা হয় Shannon’s maxim: “the enemy knows the system”। Shannon-এর সেই একই paper-এ one-time pad-এর perfect-secrecy প্রমাণও আছে — নিচের experiment-এ সেটাই হাতেকলমে দেখব।

কেন “algorithm গোপন রাখা” (security through obscurity) ব্যর্থ হয়

সহজাত প্রতিক্রিয়া অনেকেরই হয়: “algorithমটাও যদি গোপন রাখি, তাহলে তো নিরাপত্তার আরেকটা স্তর যোগ হলো — ক্ষতি কী?” যুক্তিটা ভুল, তিনটা কারণে:

  1. Algorithm এক-না-এক-দিন ফাঁস হয়ই। Reverse-engineering, decompilation, disassembly, insider leak, court-order-এ code disclosure, বা শুধু একটা পুরনো binary কেউ খুঁজে বের করা — ইতিহাসে কোনো “গোপন algorithm” চিরকাল গোপন থাকেনি। নিচের example section-এ তিনটা বাস্তব ঘটনা দেখব।
  2. গোপন algorithm-এর peer review হয় না। Public algorithm হাজার হাজার cryptographer বছরের পর বছর ধরে আক্রমণ করার চেষ্টা করে — দুর্বলতা পেলে প্রকাশ্যে জানানো হয়, algorithm আপডেট বা প্রত্যাখ্যাত হয় deployment-এর আগেই (যেমন AES নির্বাচনের NIST competition, ১৯৯৭–২০০১)। গোপন algorithm-এ এই সুযোগ নেই — দুর্বলতা টিকে থাকে যতক্ষণ না কেউ (প্রায়ই attacker-ই) নিজে খুঁজে বের করে, প্রায়ই বহু বছর পর।
  3. চাবি বদলানো সস্তা, algorithm বদলানো ব্যয়বহুল। Key compromise হলে নতুন key generate করে বিতরণ করা যায় ঘণ্টাখানেকে। Algorithm compromise হলে পুরো fleet-এ নতুন implementation deploy, নতুন certification, পুরনো ডেটা-র backward-compatibility — মাসের পর মাসের কাজ। তাই secrecy-র “budget” বুদ্ধিমানের মতো key-তে রাখা উচিত, algorithm-এ না।

উদাহরণ

বাস্তব ইতিহাসে obscurity-র পরাজয়

তিনটা ভিন্ন দশক, তিনটা ভিন্ন domain — একই প্যাটার্ন প্রতিবার।

Caseকী গোপন রাখা হয়েছিলকী ঘটল
GSM A5/1 (১৯৮৭)পুরো stream-cipher design প্রকাশ্যে না জানানো — শুধু manufacturer-দের NDA-তে দেওয়া১৯৯৯-এ reverse-engineer হয়ে যায়; ২০০০ সালের মধ্যেই Biryukov-Shamir-Wagner সাধারণ PC-তে real-time-এ ভেঙে ফেলেন — প্রায় দশক-জোড়া “গোপনীয়তা” মাত্র কয়েক বছরের গবেষণায় শেষ
DVD CSS (১৯৯৬)৪০-বিট cipher, শুধু licensed manufacturer-দের কাছে algorithm, বাকি জগতের কাছে “black box”১৯৯৯-এ DeCSS leak-এর পরপরই Frank Stevenson-এর মতো গবেষকরা দিনের মধ্যেই দুর্বলতা বের করে ফেলেন — গোপন রাখাটাই ছিল একমাত্র সুরক্ষা, algorithm নিজে দুর্বল ছিল
MIFARE Classic (Crypto1) (১৯৯৪)RFID card-এর proprietary cipher, বছরের পর বছর undocumented রাখা২০০৮-এ reverse-engineer হওয়ার পরে ধরা পড়ে ৪৮-বিট key আর দুর্বল random-number generation — লক্ষ লক্ষ transit-card ও access-badge deployment ঝুঁকিতে পড়ে যায়

তিনটা ঘটনাতেই লক্ষ্য করুন — সমস্যাটা শুধু “কারো algorithm জানা হয়ে গেল” তা নয়। সমস্যাটা হলো: algorithm ডিজাইন করার সময় ধরেই নেওয়া হয়নি যে সেটা প্রকাশ্য হবে — তাই সেটা কখনো সেই assumption-এর বিরুদ্ধে পরীক্ষিত হয়নি। যেই মুহূর্তে ফাঁস হলো, দুর্বলতাগুলো তখনই প্রকাশ পেল, কারণ কেউ কখনো সেগুলো খুঁজে বের করার চেষ্টাই করেনি — প্রথম যে করল, সে ছিল attacker।

ছবিতে

  PUBLIC (adversary জানে ধরে নিন)          SECRET (শুধু এইটুকুই)
  ┌─────────────────────────────┐          ┌───────────────┐
  │ protocol spec (RFC/paper)     │          │               │
  │ algorithm design (AES/RSA/…)  │   ──►    │      KEY      │
  │ reference implementation      │          │               │
  │ source code (audited library) │          └───────────────┘
  └─────────────────────────────┘
     এই স্তরে secrecy রাখলে —              এই এক স্তরেই যদি secrecy
     obscurity, ভঙ্গুর, unauditable        কেন্দ্রীভূত — Kerckhoffs-compliant
কী প্রকাশ্য থাকা উচিত, কী গোপন — Kerckhoffs-compliant ডিজাইনে একমাত্র 'key' layer-ই গোপন।
একটা attacker কী কী জানে ধরে নিয়ে design করবেন — নিচ থেকে ওপরে সব public, শুধু এক স্তর secret
  1. key materialএকমাত্র secret — এইটুকু compromise হলেই এবং শুধু তখনই system compromise
  2. ciphertext / observed trafficadversary পুরোটা দেখতে পারে ধরে নিন — network-এ যা যায় তা public
  3. implementation (audited library-র binary/source)reverse-engineer বা open-source — এটাও public ধরে নিন
  4. algorithm / protocol design (RFC, paper, standard)প্রথম থেকেই publish করা — obscurity-র কোনো bonus নেই

নিজে চালিয়ে দেখুন

EXPERIMENT

Obscurity বনাম Key — একই ধরনের attacker, দুইটা সম্পূর্ণ ভিন্ন ফলাফল

macOS / Linux / Windows (Python 3)· ২০ মিনিট

পার্ব ১ — Monoalphabetic substitution: “mapping-টাই secret”। এই cipher-এ প্রতিটা অক্ষরকে আরেকটা অক্ষরে বদলে দেওয়া হয় একটা secret mapping দিয়ে। Attacker-এর কাছে শুধু ciphertext, mapping জানে না।

# obscurity_vs_key.py — obscurity (mapping গোপন) বনাম Kerckhoffs (key গোপন, algorithm public)
import collections, random, string

ENGLISH_FREQ_ORDER = "etaoinshrdlcumwfgypbvkjxqz"

# ---- Part 1: monoalphabetic substitution — সুরক্ষা = "কেউ জানে না mapping কী" ----
random.seed(42)

def make_secret_mapping():
    letters = list(string.ascii_lowercase)
    shuffled = letters[:]
    random.shuffle(shuffled)
    return dict(zip(letters, shuffled))

def substitution_encrypt(plaintext, mapping):
    return "".join(mapping.get(ch, ch) for ch in plaintext)

PLAINTEXT = (
    "the enemy will attack the eastern bridge at dawn the general "
    "has ordered every soldier to hold the line until reinforcements "
    "arrive from the north the situation is critical and every hour "
    "matters to the defenders holding the bridge against the coming assault"
)

mapping = make_secret_mapping()
ciphertext = substitution_encrypt(PLAINTEXT, mapping)

print("ciphertext (attacker-এর কাছে শুধু এইটুকুই আছে, mapping গোপন):")
print(ciphertext)

counts = collections.Counter(c for c in ciphertext if c.isalpha())
cipher_freq_order = "".join(letter for letter, _ in counts.most_common())

print()
print(f"ciphertext letter-freq order (most->least) : {cipher_freq_order}")
print(f"English reference freq order                : {ENGLISH_FREQ_ORDER}")

guess_map = dict(zip(cipher_freq_order, ENGLISH_FREQ_ORDER))
guessed_plain = "".join(guess_map.get(c, c) for c in ciphertext)

print()
print("frequency-analysis guess (key কখনো দেখা যায়নি, শুধু letter-count থেকে):")
print(guessed_plain)

total_letters = sum(1 for p in PLAINTEXT if p.isalpha())
correct = sum(1 for g, p in zip(guessed_plain, PLAINTEXT) if p.isalpha() and g == p)
pct = 100.0 * correct / total_letters
print()
print(f"সঠিকভাবে উদ্ধার হওয়া letter: {correct}/{total_letters} ({pct:.1f}%)")

# ---- Part 2: OTP-style toy — সুরক্ষা = secret key, algorithm সম্পূর্ণ public ----
print()
print("=" * 60)

def xor_bytes(a, b):
    return bytes(x ^ y for x, y in zip(a, b))

def otp_encrypt(key, plaintext_bytes):
    assert len(key) == len(plaintext_bytes)
    return xor_bytes(key, plaintext_bytes)

REAL_MSG = b"ATTACK AT DAWN "
random.seed(7)
REAL_KEY = bytes(random.randrange(256) for _ in range(len(REAL_MSG)))
otp_cipher = otp_encrypt(REAL_KEY, REAL_MSG)

print(f"real plaintext : {REAL_MSG!r}")
print(f"otp ciphertext : {otp_cipher.hex()}")
print()
print("attacker পুরো algorithm জানে (equal-length key দিয়ে byte-wise XOR),")
print("এই ciphertext-ও আছে, কিন্তু REAL_KEY জানে না — চারটা candidate try করে দেখি:")

candidates = [
    b"ATTACK AT DAWN ",
    b"RETREAT AT ONCE",
    b"SEND MORE TROOP",
    b"HOLD THE BRIDGE",
]
for cand in candidates:
    implied_key = xor_bytes(otp_cipher, cand)
    reencrypted = otp_encrypt(implied_key, cand)
    consistent = reencrypted == otp_cipher
    print(f"  candidate={cand!r} -> implied_key={implied_key.hex()} -> consistent={consistent}")

প্রত্যাশিত output (deterministic — হুবহু এটাই আসবে):

ciphertext (attacker-এর কাছে শুধু এইটুকুই আছে, mapping গোপন):
ekt totbd ipss qeeqjl ekt tqyetro mrpzft qe zqio ekt ftotrqs kqy xrztrtz thtrd yxszptr ex kxsz ekt spot voeps rtpogxrjtbtoey qrrpht grxb ekt oxrek ekt ypevqepxo py jrpepjqs qoz thtrd kxvr bqeetry ex ekt ztgtoztry kxszpof ekt mrpzft qfqpoye ekt jxbpof qyyqvse

ciphertext letter-freq order (most->least) : terpqokxyzsfbjvdhgiml
English reference freq order                : etaoinshrdlcumwfgypbvkjxqz

frequency-analysis guess (key কখনো দেখা যায়নি, শুধু letter-count থেকে):
tse eneuf poll ittimv tse eirtean baodce it dipn tse ceneail sir hadeaed egeaf rhldoea th shld tse lone wntol aeonyhameuentr iaaoge yahu tse nhats tse rotwitohn or maotomil ind egeaf shwa uittear th tse deyendear shldonc tse baodce icionrt tse mhuonc irriwlt

সঠিকভাবে উদ্ধার হওয়া letter: 96/216 (44.4%)

============================================================
real plaintext : b'ATTACK AT DAWN '
otp ciphertext : e4199e59667b9b5c3933689f816d5b

attacker পুরো algorithm জানে (equal-length key দিয়ে byte-wise XOR),
এই ciphertext-ও আছে, কিন্তু REAL_KEY জানে না — চারটা candidate try করে দেখি:
  candidate=b'ATTACK AT DAWN ' -> implied_key=a54dca182530bb1d6d132cded6237b -> consistent=True
  candidate=b'RETREAT AT ONCE' -> implied_key=b65cca0b233acf7c786748d0cf2e1e -> consistent=True
  candidate=b'SEND MORE TROOP' -> implied_key=b75cd01d4636d40e7c133ccdce220b -> consistent=True
  candidate=b'HOLD THE BRIDGE' -> implied_key=ac56d21d462fd31919713ad6c52a1e -> consistent=True

দুইটা ফলাফল পাশাপাশি রাখুন। পার্ট ১-এ attacker key কখনো দেখেইনি — শুধু ciphertext-এর letter-count গুনে, তাকে English-এর স্বাভাবিক letter- frequency-র সাথে মিলিয়ে, কোনো “cryptanalysis” ছাড়াই ৯৬/২১৬ মানে ৪৪.৪% অক্ষর সরাসরি সঠিক বসিয়ে ফেলেছে — আর বাকি অংশও এতটাই কাছাকাছি (tse = “the”, eneuf = “enemy”) যে বাকি সংশোধন একটা মানুষের চোখেই কয়েক মিনিটের কাজ। Mapping “গোপন” ছিল ঠিকই, কিন্তু গোপনীয়তাটা কোনো বাস্তব সুরক্ষা দেয়নি — কারণ cipher-এর নকশাটাই structure ফাঁস করে (প্রতিটা plaintext-অক্ষর সবসময় একই ciphertext-অক্ষরে যায়, তাই frequency shape অবিকৃত থেকে যায়)।

পার্ট ২-এ ঠিক উল্টো: attacker-কে পুরো algorithm দিয়েই দেওয়া হয়েছে (byte-wise XOR, equal-length key — এক বাক্যে বর্ণনাযোগ্য, কোনো গোপন কৌশল নেই), তার কাছে real ciphertext-ও আছে। তবু চারটা সম্পূর্ণ ভিন্ন candidate plaintext-ই (ATTACK AT DAWN, RETREAT AT ONCE, SEND MORE TROOP, HOLD THE BRIDGE) একেকটা আলাদা implied_key-র সাথে সেই একই ciphertext তৈরি করে — মানে ciphertext একা দেখে বলার কোনো উপায় নেই কোনটা আসল বার্তা। প্রতিটা candidate-ই সমান সম্ভাব্য, যতক্ষণ না real key জানা যাচ্ছে। এটাই One-Time-Pad- এর বিখ্যাত perfect secrecy ধর্ম, Shannon ১৯৪৯-এ যা প্রমাণ করেছিলেন — algorithm সম্পূর্ণ প্রকাশ্য থাকা সত্ত্বেও।

এটা কী প্রমাণ করে

যেই cipher-এর সুরক্ষা শুধু 'mapping গোপন' (obscurity)-এর ওপর নির্ভর করে, সেটা ciphertext-এর letter-frequency দিয়েই বড় অংশ ভেঙে ফেলা যায় — key-ই কখনো লাগে না। আর যেই cipher-এর সুরক্ষা একটা সত্যিকারের random key-র ওপর — সেখানে পুরো algorithm জানা থাকলেও, প্রতিটা সম্ভাব্য plaintext একই ciphertext-এর সাথে সমান-ভাবে 'consistent' — ciphertext একাই কিছু প্রকাশ করে না।

নিজে বানান

BUILD IT

KerckhoffsDiagnostic — ciphertext নিজেই কতটা গঠন ফাঁস করছে তা মাপা

Python · ●●○○○
  1. একটা chi-squared-ধাঁচের uniformity স্কোর লিখুন — perfectly-uniform distribution-এর তুলনায় ciphertext-এর symbol-count কতটা skewed
  2. KerckhoffsDiagnostic ক্লাস লিখুন — স্কোর একটা threshold পার হলে "LEAKY" বলে ফ্ল্যাগ করবে, নাহলে "flat"
  3. আগের experiment-এর substitution-cipher ciphertext-এ চালান — expect: LEAKY
  4. আগের experiment-এর otp-style ciphertext-এ চালান — expect: flat
  5. raw unencrypted plaintext-এ চালান তুলনার জন্য — expect: সবচেয়ে বেশি LEAKY

লক্ষ্য: একটা reusable diagnostic tool বানানো যেটা পুরো cryptanalysis না করেই বলে দিতে পারে একটা ciphertext-এর “আকৃতি” (letter/byte-distribution) কতটা plaintext-এর ছাপ ধরে রেখেছে — experiment-এর দুইটা ciphertext-ই আবার ব্যবহার করে দেখাই diagnostic ঠিক জায়গায় alarm তোলে কি না।

# kerckhoffs_diagnostic.py — ciphertext-এর symbol-distribution কতটা "flat" তা মাপার diagnostic
import collections, random, string

def chi_square_uniformity(symbols, alphabet_size):
    """Perfectly uniform distribution-এর তুলনায় chi-squared statistic।
    কম স্কোর -> uniform-এর কাছাকাছি (ভালো, statistical leak নেই)।
    বেশি স্কোর -> distribution skewed -> plaintext-এর 'আকৃতি' encryption-এর পরেও টিকে আছে।"""
    n = len(symbols)
    counts = collections.Counter(symbols)
    expected = n / alphabet_size
    return sum((counts.get(s, 0) - expected) ** 2 / expected for s in range(alphabet_size))


class KerckhoffsDiagnostic:
    """Ciphertext-এর 'আকৃতি' চেকার। একটা cipher obscurity-নির্ভর নাকি
    proper key-নির্ভর তা বুঝতে পুরো cryptanalysis লাগে না — শুধু এই check-ই
    alarm তোলে: symbol-distribution flat না হলে, structure এখনো ফাঁস হচ্ছে।"""
    def __init__(self, alphabet_size, leak_threshold):
        self.alphabet_size = alphabet_size
        self.leak_threshold = leak_threshold

    def check(self, label, symbols):
        score = chi_square_uniformity(symbols, self.alphabet_size)
        leaky = score > self.leak_threshold
        verdict = "LEAKY (structure এখনো দেখা যাচ্ছে)" if leaky else "flat (uniform-এর কাছাকাছি)"
        print(f"  {label:32s} chi2={score:9.1f}  -> {verdict}")
        return leaky


# ---- আগের experiment-এর দুইটা ciphertext-ই পুনরায় ব্যবহার করছি ----
random.seed(42)

def make_secret_mapping():
    letters = list(string.ascii_lowercase)
    shuffled = letters[:]
    random.shuffle(shuffled)
    return dict(zip(letters, shuffled))

PLAINTEXT = (
    "the enemy will attack the eastern bridge at dawn the general "
    "has ordered every soldier to hold the line until reinforcements "
    "arrive from the north the situation is critical and every hour "
    "matters to the defenders holding the bridge against the coming assault"
)
mapping = make_secret_mapping()
sub_ciphertext = "".join(mapping.get(ch, ch) for ch in PLAINTEXT)
sub_symbols = [ord(c) - ord('a') for c in sub_ciphertext if c.isalpha()]

random.seed(7)
otp_key = bytes(random.randrange(256) for _ in range(len(PLAINTEXT.encode())))
otp_cipher = bytes(p ^ k for p, k in zip(PLAINTEXT.encode(), otp_key))
otp_symbols = list(otp_cipher)

diag26 = KerckhoffsDiagnostic(alphabet_size=26, leak_threshold=200.0)
print("substitution-cipher ciphertext (obscurity: mapping-ই একমাত্র secret):")
diag26.check("mono-alphabetic substitution", sub_symbols)

diag256 = KerckhoffsDiagnostic(alphabet_size=256, leak_threshold=600.0)
print()
print("otp-style ciphertext (Kerckhoffs: algorithm public, শুধু key secret):")
diag256.check("otp xor-stream", otp_symbols)

print()
print("raw plaintext bytes, তুলনার জন্য (কোনো encryption-ই হয়নি):")
plain_symbols = list(PLAINTEXT.encode())
diag256.check("plaintext (unencrypted)", plain_symbols)

প্রত্যাশিত output (deterministic — হুবহু এটাই আসবে):

substitution-cipher ciphertext (obscurity: mapping-ই একমাত্র secret):
  mono-alphabetic substitution     chi2=    221.2  -> LEAKY (structure এখনো দেখা যাচ্ছে)

otp-style ciphertext (Kerckhoffs: algorithm public, শুধু key secret):
  otp xor-stream                   chi2=    281.8  -> flat (uniform-এর কাছাকাছি)

raw plaintext bytes, তুলনার জন্য (কোনো encryption-ই হয়নি):
  plaintext (unencrypted)          chi2=   5096.2  -> LEAKY (structure এখনো দেখা যাচ্ছে)

তিনটা সংখ্যা পাশাপাশি রাখলে গল্পটা স্পষ্ট: raw plaintext সবচেয়ে বেশি skewed (chi2=5096.2 — ভাষার নিজস্ব letter-frequency-র কারণে), substitution cipher সেই skew-এর একটা rotated সংস্করণ মাত্র ধরে রাখে (chi2=221.2 — কমেছে, কিন্তু threshold-এর অনেক ওপরে, তাই LEAKY), আর OTP-style ciphertext প্রায় uniform (chi2=281.8, ২৫৬-অক্ষরের alphabet-এর জন্য অনেক বেশি threshold-এই এটা flat)। Threshold সংখ্যাগুলো এখানে টিচিং-উদ্দেশ্যে বাছা হয়েছে — বাস্তব cryptanalysis-এ formal chi-squared p-value table ব্যবহার হয়, কিন্তু ধারণাটা এক: cipher যত ভালো, ciphertext তত বেশি uniform দেখাবে, plaintext-এর “আকৃতি” যত কম টিকে থাকবে।

নিজে বাড়ান:

  1. chi_square_uniformity-কে bigram (দুই-অক্ষরের জোড়া) distribution-এ চালান — single-letter frequency লুকালেও bigram pattern (th, he, in) কি টিকে থাকে?
  2. Threshold-গুলো বাস্তব chi-squared distribution table থেকে সঠিক p-value (যেমন ৯৫% confidence) দিয়ে বসান — কত degrees-of-freedom (`alphabet_size
    • 1`) ব্যবহার করবেন?
  3. KerckhoffsDiagnostic-কে CBC/ECB-mode ciphertext-এ (আগের symmetric- encryption লেসনের experiment থেকে) চালিয়ে দেখুন — block-repetition কি এই diagnostic ধরতে পারে, নাকি আলাদা metric লাগবে?

বাস্তব সিস্টেমে

বাস্তব জগতে Kerckhoffs — আর এই module-এর নিজস্ব দ্বন্দ্ব

Open standardization. AES নির্বাচিত হয়েছিল NIST-এর একটা প্রকাশ্য প্রতিযোগিতায় (১৯৯৭–২০০১) — বিশ্বের যেকোনো cryptographer submission জমা দিতে ও অন্যদের submission আক্রমণ করতে পারতেন। TLS 1.3, HTTPS, SSH — সবগুলোর protocol spec (RFC) সম্পূর্ণ প্রকাশ্য, কেউ চাইলে আজই পুরো handshake পড়ে ফেলতে পারেন। এটাই Kerckhoffs-compliant ডিজাইনের আধুনিক default।

Audited library। OpenSSL, libsodium, BoringSSL, প্রতিটা ভাষার standard crypto module — এগুলোর algorithm প্রকাশ্য, source code প্রকাশ্য, বছরের পর বছর ধরে security researcher-রা fuzz করেছেন, audit করেছেন। Kerckhoffs-এর নীতি বলে secrecy শুধু key-তে থাকা উচিত — কিন্তু সেই key ঠিকভাবে ব্যবহার করা (nonce, IV, padding, mode — আগের লেসনগুলোর বিষয়) একটা আলাদা, সমান-জরুরি প্রশ্ন। তাই “audited library ব্যবহার করুন, নিজে লিখবেন না” পরামর্শটা Kerckhoffs-এর সরাসরি সম্প্রসারণ — algorithm secrecy না, বরং সঠিক implementation-ই আসল ঝুঁকির জায়গা, আর সেটা যাচাই করার একমাত্র উপায় হলো বহু চোখ দিয়ে প্রকাশ্যে পর্যালোচনা।

এই module-এর crypto toolbox — সংক্ষিপ্ত roadmap

আজকের ভিত্তির ওপর দাঁড়িয়ে বাকি module কীভাবে এগোবে:

পরবর্তী লেসনকী সমাধান করেআজকের কোন goal-এর সাথে যুক্ত
Hash functionsবড় ডেটার জন্য fixed-size fingerprint — integrity যাচাই key ছাড়াইIntegrity
Symmetric encryption (আগেই লেখা)একই key দিয়ে encrypt/decrypt — AES, block cipher mode (ECB/CBC/CTR/GCM)Confidentiality (+ GCM-এ integrity/authenticity)
Asymmetric encryptionভিন্ন encrypt/decrypt key — অচেনা দুইজন প্রকাশ্য channel দিয়েই secret-এ একমত হওয়াConfidentiality-র chicken-and-egg সমাধান
Digital signatures / PKIprivate key দিয়ে sign, public key দিয়ে verifyAuthenticity + Non-repudiation
TLSওপরের সবগুলো একসাথে জোড়া লাগিয়ে একটা প্রকৃত secure channelচারটা goal-ই একসাথে, বাস্তব protocol-এ

প্রতিটা ধাপ আগেরটার ওপর দাঁড়ায় — hash function ছাড়া signature সম্ভব না (বড় ডেটা সরাসরি sign করা ব্যয়বহুল, তাই আগে hash), symmetric ছাড়া TLS সম্ভব না (asymmetric ধীর, তাই handshake-এর পরে বাল্ক ডেটা symmetric-এই যায়)। আজকের দুইটা প্রশ্ন — “নিরাপত্তা মানে কী” আর “নিরাপত্তা কীসের ওপর নির্ভর করে” — প্রতিটা লেসনেই ফিরে আসবে, উত্তরটা প্রতিবার একটু concrete হবে।

যে ভুলগুলো সবাই করে

“Kerckhoffs's Principle মানে algorithm গোপন রাখা নিষিদ্ধ — সব প্রকাশ্য না করলে সেটা 'insecure by design'।”

ভুল ব্যাখ্যা। Kerckhoffs বলছেন না algorithm গোপন রাখা অন্যায় — বলছেন নিরাপত্তা সেই গোপনীয়তার ওপর নির্ভর করা উচিত না। একটা কোম্পানি চাইলে তাদের implementation বাণিজ্যিক কারণে বন্ধ (closed-source) রাখতে পারে — সমস্যা তখনই হয় যখন সেই algorithm প্রকাশ্য হয়ে গেলে system ভেঙে পড়ে। প্রশ্নটা মালিকানার না, নির্ভরতার।

“Open-source crypto library ব্যবহার করলেই আমার system নিরাপদ — audit করার আর দরকার নেই।”

Open-source হওয়াটা peer-review সম্ভব করে, নিজে থেকে peer-review হয়ে গেছে তার গ্যারান্টি দেয় না। Heartbleed (OpenSSL, ২০১৪) বছরের পর বছর প্রকাশ্য কোডে লুকিয়ে ছিল — কেউ সেই অংশটা মনোযোগ দিয়ে দেখেনি। “Public হওয়া” যাচাইয়ের সুযোগ দেয়; সেই সুযোগ কেউ ব্যবহার করেছে কি না সেটা আলাদা প্রশ্ন — বহুল-ব্যবহৃত, সক্রিয়ভাবে-maintained, নিয়মিত-audited library বাছা এখনো জরুরি।

“ছোট, ব্যক্তিগত প্রজেক্টে নিজের বানানো cipher ব্যবহার করা ঠিক আছে, যতক্ষণ সেটা কাউকে দেখানো হচ্ছে না।”

Experiment-টাই এর বিপক্ষে সরাসরি প্রমাণ — attacker-এর “algorithm না জানা” কোনো নির্ভরযোগ্য সুরক্ষা দেয়নি, শুধু ৪৪.৪% অক্ষর একাই frequency-count দিয়ে বেরিয়ে এসেছে। ছোট প্রজেক্ট হলেও যদি আসল sensitive ডেটা (password, personal data, financial record) জড়িত থাকে, “কেউ দেখছে না” ধরে নেওয়া নিজেই একটা ঝুঁকি — কোড leak, backup exposure, বা শুধু কৌতূহলী কেউ ঘেঁটে দেখলেই ধরা পড়ে। নিয়ম একই থাকে স্কেল-নির্বিশেষে: production না হলেও, sensitive ডেটার জন্য audited primitive ব্যবহার করুন।

“CIA Triad-ই cryptography-র পুরো গল্প বলে দেয় — confidentiality, integrity, availability থাকলেই যথেষ্ট।”

Triad ঐতিহাসিকভাবে গুরুত্বপূর্ণ কিন্তু cryptography-র জন্য অসম্পূর্ণ — এতে authenticity (sender কে, তা যাচাই) আর non-repudiation (sender পরে অস্বীকার করতে না পারা) স্পষ্টভাবে নেই। Phishing email-এর উদাহরণ ঠিক এইখানে ধরা পড়ে — confidentiality, integrity দুইটাই অক্ষত থাকতে পারে, তবু authenticity ভেঙে পুরো আক্রমণ সফল হয়ে যায়। আজকের চারটা goal-ই একসাথে বিবেচনা না করলে এই ধরনের ফাঁক অদৃশ্যই থেকে যায়।

বুঝেছেন কি না দেখুন

1

Confidentiality আর integrity — এই দুইটা security goal-এর সংজ্ঞা কী, আর কোন উদাহরণে একটা ভঙ্গ হয় অথচ অন্যটা অক্ষত থাকে?

স্মরণ

Confidentiality: শুধু অনুমোদিত পক্ষ content পড়তে পারবে। Integrity: ডেটা unauthorized-ভাবে বদলায়নি, বদলালে ধরা পড়বে। উদাহরণ: bank-transfer amount ১০০ থেকে ১০০০ বদলে দেওয়া — attacker content পড়তে পারেনি (confidentiality অক্ষত, ডেটা encrypted ছিল), কিন্তু মাঝপথে content বদলে গেছে (integrity ভঙ্গ)।

2

Kerckhoffs’s Principle বলে নিরাপত্তা শুধু key-এর ওপর নির্ভর করা উচিত। GSM A5/1 আর DVD CSS-এর ইতিহাস কীভাবে এই নীতিটাকে সমর্থন করে?

যুক্তি

দুইটা cipher-ই ডিজাইন করা হয়েছিল algorithm গোপন থাকবে এই ধরে নিয়ে — প্রকাশ্য আক্রমণের বিরুদ্ধে কখনো পরীক্ষিত হয়নি। যেই মুহূর্তে reverse- engineer হয়ে গেল (A5/1: ১৯৯৯, CSS: DeCSS-এর পরে), দুর্বলতাগুলো প্রায় সাথে সাথেই ধরা পড়ল এবং ভাঙা গেল — কারণ algorithm-এর নিজস্ব শক্তি কখনো যাচাই হয়নি, শুধু গোপনীয়তার ওপর নির্ভর করেছিল।

3

Experiment-এর substitution cipher-এ attacker key ছাড়াই ৪৪.৪% অক্ষর সঠিক বসিয়ে ফেলেছে, অথচ OTP-স্টাইল ciphertext-এ চারটা ভিন্ন candidate plaintext-ই সমান-সম্ভাব্য থেকে গেছে। এই দুইটার পার্থক্যের মূল কারণ কী — শুধু “key লম্বা কি ছোট” তা না হলে, তাহলে কী?

প্রয়োগ

পার্থক্যটা key-এর দৈর্ঘ্যে না, cipher-এর structural গুণে। Substitution cipher-এ প্রতিটা plaintext-অক্ষর সবসময় একই ciphertext- অক্ষরে ম্যাপ হয় — তাই ভাষার letter-frequency shape অবিকৃত থেকে যায়, attacker সেই shape-ই ধরে ফেলে। OTP-স্টাইল XOR-এ প্রতিটা ciphertext-বাইট নির্ভর করে একটা independent, uniformly-random key-বাইটের ওপর — তাই ciphertext-এর নিজস্ব কোনো “shape” অবশিষ্ট থাকে না, প্রতিটা equal-length plaintext-ই সমান-সম্ভাব্য থাকে।

4

“আমাদের দলের বানানো custom encryption scheme কেউ চেনে না, তাই এটা industry-standard AES-এর চেয়েও নিরাপদ” — এই দাবিতে Kerckhoffs’s Principle অনুযায়ী কোন যুক্তিগত ভুল আছে?

যুক্তি

দাবিটা secrecy-র উৎস গুলিয়ে ফেলছে — নিরাপত্তা algorithm অজানা থাকার ওপর নির্ভর করছে, key-র ওপর না। এই “custom” scheme কখনো প্রকাশ্য cryptanalysis- এর মুখোমুখি হয়নি, তাই এর দুর্বলতা অজানাই থেকে যাচ্ছে — “কেউ ভাঙতে পারেনি” এর মানে “কেউ চেষ্টা করেনি”ও হতে পারে। AES প্রকাশ্য NIST প্রতিযোগিতায় বছরের পর বছর বিশ্বের সেরা cryptographer-দের আক্রমণ সহ্য করে টিকে আছে — সেই পরীক্ষাটাই “নিরাপদ” দাবির ভিত্তি, শুধু জনপ্রিয়তা না।

5

KerckhoffsDiagnostic টুল substitution-cipher ciphertext-কে chi2=221.2 দিয়ে LEAKY, আর OTP-ciphertext-কে chi2=281.8 দিয়ে flat হিসেবে চিহ্নিত করেছে — সংখ্যা দুইটা কাছাকাছি হলেও রায় উল্টো কেন?

প্রয়োগ

দুইটা ভিন্ন alphabet-size-এর জন্য ভিন্ন threshold ব্যবহার হয়েছে — substitution cipher ২৬-অক্ষরের alphabet-এ (leak_threshold=200.0), OTP-ciphertext ২৫৬-বাইটের alphabet-এ (leak_threshold=600.0)। বড় alphabet-এ chi-squared statistic-এর স্বাভাবিক ওঠানামা (variance) অনেক বেশি, তাই একই raw স্কোর ছোট আর বড় alphabet-এ ভিন্ন অর্থ বহন করে — তুলনা করতে হলে alphabet-size অনুযায়ী threshold সমন্বয় করা জরুরি, নাহলে ভুল সিদ্ধান্ত আসবে।

6

একটা নতুন startup আপনাকে বলল: “আমরা AES-256 ব্যবহার করছি, আমাদের algorithm নিরাপদ, তাই confidentiality, integrity, authenticity, non-repudiation — চারটাই আমাদের আছে।” এই দাবি যাচাই করতে আপনি কোন প্রশ্নগুলো জিজ্ঞেস করবেন, আর কেন শুধু “AES ব্যবহার করছি” যথেষ্ট উত্তর না?

ডিজাইন

AES শুধু একটা block cipher — নিজে থেকে শুধু confidentiality-র দিকে অবদান রাখে (আর সেটাও mode-এর ওপর নির্ভরশীল, আগের লেসনের বিষয়)। প্রশ্ন করতে হবে: (১) কোন mode ব্যবহার হচ্ছে — GCM/AEAD হলে integrity ও কিছুটা authenticity আসে, plain CBC/CTR হলে আসে না; (২) authenticity-র জন্য আলাদা mechanism আছে কি (signature বা MAC, পরের লেসনের বিষয়) — শুধু encryption দিয়ে sender কে তা প্রমাণ হয় না; (৩) non-repudiation-এর জন্য digital signature বা audit-trail আছে কি — symmetric encryption-এ এটা সম্ভবই না, কারণ দুই পক্ষই একই key জানে, তাই কে পাঠাল তা cryptographically প্রমাণযোগ্য না। AES-256 থাকা মানেই চারটা goal অর্জিত — এই অনুমানটাই ভুল, প্রতিটা goal-এর জন্য নির্দিষ্ট mechanism দরকার, যেগুলো এই module-এর পরের লেসনগুলোতে আসছে।

এরপর কী

এরপর কী

আজকে দুইটা ভিত্তি পাকা হলো: চারটা goal (confidentiality, integrity, authenticity, non-repudiation) আলাদা প্রশ্ন হিসেবে চেনা, আর Kerckhoffs’s Principle — নিরাপত্তা কখনো algorithm-এর গোপনীয়তায় না, শুধু key-তে থাকা উচিত। এই দুইটা প্রশ্নই এখন থেকে প্রতিটা crypto primitive-এর জন্য জিজ্ঞেস করবেন: “এটা কোন goal দেয়?” আর “এর নিরাপত্তা ঠিক কী গোপন থাকার ওপর নির্ভর করে?”

Toolbox-এর প্রথম concrete primitive দিয়েই শুরু হবে পরের লেসন: hash function — এমন এক ফাংশন যা একটা shared worry (বড় ডেটার integrity যাচাই করা কীভাবে, পুরো ডেটা বারবার তুলনা না করেই) কে একটা fixed-size fingerprint- এ রূপান্তর করে। Hash function-এর নিজের কোনো key লাগে না — কিন্তু ঠিক Kerckhoffs-এর মতোই, এর নিরাপত্তাও নির্ভর করে একটা প্রকাশ্যে-পরীক্ষিত design-এর ওপর, কোনো গোপন কৌশলের ওপর না। আগে থেকে লেখা symmetric-encryption লেসনের E(key, plaintext) মনে আছে তো — hash function সেই একই জগতের প্রথম ইট, শুধু চাবি ছাড়াই।

আরও পড়ুন

  • La Cryptographie Militaire — Auguste Kerckhoffs — Journal des sciences militaires, vol. IX, 1883 · মূল প্রবন্ধ — ছয়টা desiderata-র দ্বিতীয়টাই আজকের 'Kerckhoffs's Principle', ইংরেজি অনুবাদসহ Fabien Petitcolas-এর সংরক্ষিত সংস্করণ
  • Communication Theory of Secrecy Systems — Claude E. Shannon — Bell System Technical Journal 28(4), 1949 · 'The enemy knows the system' — Kerckhoffs-এর নীতিরই তথ্যতাত্ত্বিক পুনর্গঠন, আর one-time-pad-এর perfect-secrecy প্রমাণ এই paper-েই
  • Real Time Cryptanalysis of A5/1 on a PC — Alex Biryukov, Adi Shamir, David Wagner — FSE 2000 · GSM-এর গোপন-রাখা A5/1 cipher reverse-engineer হওয়ার পরেই কার্যত সেকেন্ডে ভেঙে ফেলা গিয়েছিল — obscurity-র ব্যর্থতার সংখ্যাভিত্তিক প্রমাণ
  • Cryptanalysis of Contents Scrambling System — Frank A. Stevenson, 1999 · DVD-র CSS cipher (৪০-বিট, প্রকাশ্যে অনালোচিত রাখা) DeCSS leak-এর পরে কীভাবে দিনের মধ্যেই ভাঙা হয় তার বিশ্লেষণ