أنماط التشغيل في تشفير الكتل

كتبه بركات يوم الأحد, 25 تشرين الأول 2015

معظم خوارزميات التشفير الحديثة مثل AES و 3DES تشفر كتل بطول ثابت، فمثلأً في AES حجم هذه الكتلة 128 بت، و 64 بت في 3DES، ماذا عن البيانات الأكبر من حجم الكتلة التي تشفرها الخوارزمية؟ أحد الحلول أن نقسم البيانات المراد تشفيرها إلى عدة أقسام ثم نشفر كل قسم على حدة، إذا كان حجم القسم الأخير أقل من حجم الكتلة، فنحشوها.

الطريقة السابقة تسمى ECB اختصاراً لـElectronic Codebook، وهي الطريقة البدائية لدمج تلك الكتل، فلو كان عندنا بيانات نريد تشفيرها، فنقسمها لعدة أقسام بحجم $n$، حجم كل قسم $d_i$ يساوي حجم الكتلة التي تستخدمها الخوارزمية المستخدمة للتشفير، نشفر هكذا:

$$ \begin{align} c_0 &= \textrm{Encrypt}(k, d_0) \\ & \ldots \\ c_{n-1} &= \textrm{Encrypt}(k, d_{n-1}) \\ c_{n} &= \textrm{Encrypt}(k, d_{n}) \\ \end{align} $$

ونفك التشفير هكذا:

$$ \begin{align} d_0 &= \textrm{Decrypt}(k, c_0) \\ & \ldots \\ d_{n-1} &= \textrm{Decrypt}(k, c_{n-1}) \\ d_{n} &= \textrm{Decrypt}(k, c_{n}) \\ \end{align} $$

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

ناتج التشفير

الصورة التي في اليسار صورة BMP شفرتها باستخدام AES-128 مرتين باستخدام OpenSSL، وهي مكتبة ومجموعة أدوات للتشفير ومتعلقاته كالأداة enc1:

$ openssl enc -aes-128-ecb -e -nosalt -in plain.bmp -out cipher-ecb.bmp -k Pa5sW0rd
$ openssl enc -aes-128-cbc -e -nosalt -in plain.bmp -out cipher-cbc.bmp -k Pa5sW0rd

الصورة التي في الوسط، تستخدم ECB والثالثة في اليمين تستخدم CBC، هذه تسمى أنماط التشغيل mode of operation، حيث تهتم بالعلاقة بين الكتل المشفرة بأحد تشفيرات الكتل block ciphers وليست خاصة بتشفير معين، ستلاحظ أن الشكل الخماسي واضح في الصورة التي في الوسط، بينما الصورة في اليسار مجرد ضجيج كما يفترض، رغم أنهما بنفس التشفير، إلا أن اختلاف طريقة ضم الكتل تسبب في تسريب معلومات الصورة.

الشيء الذي يجب عليك معرفته أن تتجنب ECB، وتستخدم أي نمط غيرها، اختيار النمط المناسب يعتمد على مدى احتياجك، فتلك الأنماط تختلف بعدة أشياء منها:

  • بعض تلك الأنماط تولد بيانات مختلفة حتى لو شفرت نفس الرسالة بنفس المفتاح
  • بعض تلك الأنماط تمكنك من تشفير أجزاء الملف باستقلالية عن الأجزاء الأخرى
  • بعض تلك الأنماط تمكن من فك تشفير تلك الأجزاء باستقلالية عن الأجزاء الأخرى
  • مدى تغير الملف بعد فك التشفير إذا حصل تغيير في الملف المشفر
  • بعض تلك الأنماط تمكن من التحقق من سلامة الملفات المشفرة من التعديل

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

سأتحدث هنا فقط عن CBC وهي اختصاراً لـCipher Block Chaining، لأنها بسيطة وشائعة في تشفير الملفات، في هذا النمط، وقبل تشفير البيانات الأصلية بخوارزمية التشفير، نعمل xor لكتلة البيانات الأصلية مع كتلة البيانات المشفرة السابقة، أي $d_{i} \oplus c_{i-1}$، ثم نمرر الكتلة الناتجة لخوارزمية التشفير كي تشفرها، بالنسبة للكتلة الأولى والتي لايسبقها شيء، فنولد كتلة عشوائية تسمى IV أي Initialization Vector، هذه الكتلة تعمل مقام الكتلة السابقة لأول كتلة من البيانات المراد تشفيرها، لايلزم أن تكون قيمة الـIV سرية، بل يمكن دمجها مع نفس الملف المشفر، لكن يفترض أن تكون قيمة الـIV عشوائية حتى لو شفرت نفس الرسالة بنفس المفتاح ستعطي بيانات مختلفة مع كل مرة، لأن كل مرة نستخدم IV جديد.

التشفير باستخدام CBC:

$$ \begin{align} c_0 &= \textrm{Encrypt}(k, d_0 \oplus \textrm{IV}) \\ & \ldots \\ c_{n-1} &= \textrm{Encrypt}(k, d_{n-1} \oplus c_{n-2}) \\ c_{n} &= \textrm{Encrypt}(k, d_{n} \oplus c_{n-1}) \\ \end{align} $$

سبب استخدام xor هنا عائد لخاصية مهمة في الـxor، فلو أعطيتك بت واحد عشوائي قيمته 1، فهناك احتمال 50% أنه نتج من $1 \oplus 0 = 1$ و 50% أنه نتج من $0 \oplus 1 = 1$، وكذلك لو كان 0، فمع $1 \oplus 1 = 0$ و $0 \oplus 0 = 0$، الاحتمال 50%-50% لكل منهما، في الحقيقة لو كان عندك $x$ عشوائي، وقيمة $y$ عشوائية، فإن ناتج $x \oplus y$ عشوائي أيضاُ، هذا بالإضافة لكونها سريعة التنفيذ تقريباً في كل المعالجات، لهذا نجد أن xor عنصر أساسي في التشفير الحديث.

بالنسبة لفك التشفير في CBC، فعندما نفك التشفير، سينتج لنا البيانات التي عملنا لها xor مع الكتلة السابقة، أي $d_{i} \oplus c_{i-1}$، حتى نستعيد البيانات الأصلية، نعمل xor مع الكتلة السابقة المشفرة كي نحصل على البيانات الأصلية $d_i$ أي $d_i = (d_{i} \oplus c_{i-1}) \oplus c_{i-1}$:

$$ \begin{align} d_0 &= \textrm{Decrypt}(k, c_0) \oplus \textrm{IV}\\ & \ldots \\ d_{n-1} &= \textrm{Decrypt}(k, c_{n-1}) \oplus c_{n-2}\\ d_{n} &= \textrm{Decrypt}(k, c_{n}) \oplus c_{n-1}\\ \end{align} $$

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

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


  1. استخدمت في الأمر -nosalt ، لاتستخدمها عند التشفير لأنها تعيد استخدام الـIV بدلاً من توليد واحد جديد لكل مرة تشفر بها.