التشفير

التعمية أو التشفير المُلغَّز أو الإلغاز (بالإنجليزية: Cryptography) هو فرع من علم التعمية يهتم بممارسة بعض التقنيات لتأمين عملية التواصل بوجود أشخاص أخرين والذين يسمون أعداء (adversaries). بصوره عامة، تهتم التعمية بإنشاء الأنظمة التي تمنع الأعداء أو العامة من قراءة الرسائل الخاصة. أي بوسائل تحويل البيانات (مثل الكتابة) من شكلها الطبيعي المفهوم لأي شخص إلى شكل غير مفهوم بحيث يتعذّر على من لا يملك معرفة سرية محددة معرفة فحواها.

كان غايوس يوليوس قيصر (44 ق.م) يواجه خطر تسرب المعلومات في رسائله العسكرية في حال وقعت رسائله في أيدي العدو، فابتكر وسيلة بسيطة لإخفاء مضمونها: كان يُبدل كل حرف في النص بحرف آخر يليه بعدد معين من الخانات هذه الطريقة أصبحت تُعرف اليوم بـ شيفرة قيصر (Caesar Cipher).

فإذا كان مقدار الإزاحة 3 مثلاً يصير:

وأما الحروف الأخيرة فتدور، فيكون:

ومهمتنا تطبيقها بخوارزمية تأخذ نصًّا وتعميه.

شيفرة سيزر بإزاحة مقدارها 3 خطوات

أولاً نعرف الحروف الكبيرة والصغيرة:

alphabet_lower = "abcdefghijklmnopqrstuvwxyz"
alphabet_upper = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"

تذكر أن لدينا في بايثون الفعل: str.index(sub: str) -> int حيث يأخذ هذا الفعل جُزءًا نصيًّا ويأتي على النص من أوله إلى آخر باحثًا عن موضعه. ونحن سنستعمل ذلك في البحث عن موضع الحرف فسيكون على النحو التالي:

print(alphabet_lower.index('a'))
print(alphabet_lower.index('z'))
0
25

والأمر الآخر الذي سيفيدنا في تدوير الأرقام هو:

الحسابيات المقاسية (Modular Arithmetic) تتحرك فيه الأرقام بالجمع والضرب ونحوه بحيث تلتف الأرقام حول بعضها البعض عند الوصول إلى قيمة معينة، تسمى القياس (Modulus).

نستعمل في ساعة الحائط القياس 12 في حساب الوقت

وهذه العملية في بايثون هي % وتعبر عن باقي القسمة:

(9 + 4) % 12
1

والآن نعرف الدالة التي ستأخذ حرفًا (char) ومقدار الإزاحة (shift) لتعيد الحرف البديل عنه بعد الإزاحة.

def encode(char: str, shift: int) -> str:
    if char in alphabet_lower:
        alphabet = alphabet_lower
    elif char in alphabet_upper:
        alphabet = alphabet_upper
    else:
        return char
    
    code = alphabet.index(char)
    new_code = (code + shift) % len(alphabet)
    new_char = alphabet[new_code]
    return new_char

نختبر الدالة ببعض التوكيادت:

assert encode('a', 3) == 'd'
assert encode('b', 3) == 'e'

assert encode('x', 3) == 'a'
assert encode('y', 3) == 'b'

أما دالة عكس التعمية، فهي ببساطة تعكس الإزاحة بطرح الرقم بعد جمعه.

def decode(char: str, shift: int) -> str:
    if char in alphabet_lower:
        alphabet = alphabet_lower
    elif char in alphabet_upper:
        alphabet = alphabet_upper
    else:
        return char
    
    code = alphabet.index(char)
    new_code = (code - shift) % len(alphabet)
    new_char = alphabet[new_code]
    return new_char

فأما دالة استخراج المعمى، فنريدها أن تكون فعلاً تعكِس، ولذلك سنستعمل التوكيد بتمرير المعمة لعكسه مباشرة بهذه الطريقة:

assert decode(encode('a', 3), 3) == 'a'
assert decode(encode('z', 3), 3) == 'z'

والآن بعد أن تأكدنا من عمل ذلك على مستوى الحرف الواحد، نريد أن نأخذ النص كاملاً فنعميه:

def encode_text(text: str, shift: int) -> str:
    result = ""
    for char in text:
        result += encode(char, shift)
    return result

أما عكس التعمية فباستعمال decode بدلاً من encode بمثل ما تقدَّم:

def decode_text(text: str, shift: int) -> str:
    result = ""
    for char in text:
        result += decode(char, shift)
    return result

والآن نريد أن نختبر، ولذلك سنستعين بموقع تفاعلي قد تم فيه عمل ذلك قبلنا، وهو موقع: cryptii.com فنأخذ النص ونستعمله ونتأكد أن ما نخرج به نفس ما خرجوا به:

assert (
    encode_text("If he had anything confidential to say, he wrote it in cipher, that is, by so changing the order of the letters of the alphabet, that not a word could be made out", 3) ==
    "Li kh kdg dqbwklqj frqilghqwldo wr vdb, kh zurwh lw lq flskhu, wkdw lv, eb vr fkdqjlqj wkh rughu ri wkh ohwwhuv ri wkh doskdehw, wkdw qrw d zrug frxog eh pdgh rxw"
)

أخيرًا نستعمل ذلك بعد أن تأكدنا من صحته كاملاً:

text = "If he had anything confidential to say, he wrote it in cipher, that is, by so changing the order of the letters of the alphabet, that not a word could be made out."

print("## Text")
print(text)

text_encoded = encode_text(text, 3)
text_decoded = decode_text(text_encoded, 3)

print("## Encoded")
print(text_encoded)

print("## Decoded")
print(text_decoded)
## Text
If he had anything confidential to say, he wrote it in cipher, that is, by so changing the order of the letters of the alphabet, that not a word could be made out.
## Encoded
Li kh kdg dqbwklqj frqilghqwldo wr vdb, kh zurwh lw lq flskhu, wkdw lv, eb vr fkdqjlqj wkh rughu ri wkh ohwwhuv ri wkh doskdehw, wkdw qrw d zrug frxog eh pdgh rxw.
## Decoded
If he had anything confidential to say, he wrote it in cipher, that is, by so changing the order of the letters of the alphabet, that not a word could be made out.

رسالة في استخراج المعمى

عهد الخليفة المأمون إلى الكندي، وهو أبو يُوسُفَ يَعْقُوبُ بنُ إِسْحاقَ الكِنْدِيُّ (185- 256 هـ / 801- 873 م) بإدارة بيت الحكمة، وكان ذلك بعد إتمام دراسته، حيث بدأ العمل في ترجمة المخطوطات اليونانية لأرسطو وغيره من الفلاسفة إلى اللغة العربية. واجه الكندي أول مرة خلال عمله الحاجة إلى استخراج النصوص المعماة، حيث كانت بعض المخطوطات التي كان عليه ترجمتها معماة.

قدم الكندي جدولاً بالتكرارات المطلقة لحروف الأبجدية العربية، والتي حسبها في عينة من سبع أوراق من النص.

الحرف تكراره الحرف تكراره الحرف تكراره الحرف تكراره
ا 600 ر 155 س 91 ش
ل 437 ع 131 ق 63 ض
م 320 ف 122 ح 57 خ
ه 273 ت 120 ج 46 ث 17
و 262 ب 112 ذ 35 ط 15
ي 252 ك 112 ص 32 غ 15
ن 221 د 92 خ 20 ظ 8

ملاحظة: لم يشر المؤلف إلى تكرارات حروف الشين والضاد والخاء، مع الإشارة إلى مكانها في الجدول، مرتبة حسب الترتيب التنازلي للتكرارات.

يصف المؤلف طريقة استخراج المعمى التكراري على النحو التالي:

فمما نحتال به لاستنباط الكتاب المعمى إذا عرف بأي لسان هو، أن يوجد من ذلك اللسان كتاب قدر ما يقع في جلد أو ما أشبهه، فنعد ما فيه من كل نوع من أنواع حروفه، فنكتب على أكثرها عددًا الأول، والذي يليه في الكثرة الثاني، والذي يلى ذلك في الكثرة الثالث، وكذلك حتى نأتي على جميع أنواع الحروف، ثم ننظر في الكتاب الذي نريد استخراجه فنصنف أيضًا أنواع صوره، فتنظر إلى أكثرها عددًا، فنسمه بسمة الحرف الأول، والذي يليه في الكثرة فنسمه بسمة الحرف الثاني، والذي يليه في الكثرة فنسمه بسمة الحرف الثالث، ثم كذلك حتى تنفد أنواع صور حروف الكتاب المعماة التي قصد لاستنباطه

المسألة

اكتب خوارزمية استخراج المعمى، كما بينها الكندي. وذلك يعني أننا لو جئنا بنصٍّ معمَّى بشفرة قيصر ولا تعلم مقدار الإزاحة، فإنك تستطيع أعادة النص الأصلي.