دوال اشتقاق المفاتيح
دوال اشتقاق المفاتيح
الوصف
دالة اشتقاق المفاتيح (key derivation function) هي دالة تشتقّ قيمةً سرّية واحدة أو أكثر (وهي المفاتيح) من قيمة سرّية واحدة.
وكثير من دوالّ اشتقاق المفاتيح تقبل أيضًا معامل الملح (salt)\s، وهو معامل
اختياري عادةً. ويجعل هذا المعامل دالة اشتقاق المفاتيح لا تُعيد دائمًا المفاتيح
نفسها للقيمة السرّية نفسها. وكما في أنظمة التشفير الأخرى، فإنّ الملح\s يختلف
جوهريًّا عن الدخل السرّي: فالملح لا يلزم عادةً أن يكون سرّيًا، ويمكن إعادة
استعماله.
وتُفيد دوالّ اشتقاق المفاتيح، مثلًا، عندما يبدأ بروتوكول تشفير بقيمة سرّية واحدة، مثل كلمة مرور مشتركة أو قيمة سرّية مشتقّة بواسطة تبادل المفاتيح Diffie-Hellman، لكنّه يحتاج إلى عدّة قيم سرّية ليعمل، مثل مفاتيح التشفير ومفاتيح MAC. ومن استعمالات دوالّ اشتقاق المفاتيح أيضًا المولّدات العشوائيّة الآمنة تشفيريًّا، وسنراها بمزيد من التفصيل في فصل لاحق، حيث تُستعمل لاستخراج عشوائيّة ذات كثافة إنتروبيّة عالية من مصادر كثيرة، لكلٍّ منها كثافة إنتروبيّة منخفضة.
وهناك فئتان رئيسيّتان من دوالّ اشتقاق المفاتيح، بحسب محتوى إنتروبيا القيمة السرّية، وهو ما يحدّد عدد القيم المختلفة التي يمكن أن تتّخذها القيمة السرّية.
فإذا كانت القيمة السرّية، مثلًا، كلمة مرور يقدّمها المستخدم، فإنّها تحوي عادةً
إنتروبيّة ضئيلة جدًّا. فعدد القيم التي يمكن أن تتّخذها كلمة المرور قليل جدًّا.
وكما ثبّتنا من قبل في قسم سابق عن تخزين كلمات المرور <password storage>،
فإنّ ذلك يعني أنّه من الضروري أن تكون دالة اشتقاق المفاتيح صعبة الحساب. أي أنّها
تتطلّب مقدارًا غير تافهٍ من الموارد الحاسوبيّة، مثل دورات المعالج أو الذاكرة.
فإذا كانت دالة اشتقاق المفاتيح سهلة الحساب، لأمكن للمهاجم بمجرّد أن يعدّد كلّ
القيم الممكنة للسرّ المشترك، إذ الاحتمالات قليلة، ثمّ يحسب دالة اشتقاق المفاتيح
لكلٍّ منها. وكما رأينا في ذلك القسم السابق عن تخزين كلمات المرور، فإنّ هكذا
تعمل معظم الهجمات الحديثة على مخازن كلمات المرور. واستعمال دالة اشتقاق مفاتيح
مناسبة يمنع هذه الهجمات. وسنرى في هذا الفصل scrypt، فضلًا عن دوالّ اشتقاق
مفاتيح أخرى في هذه الفئة.
ومن ، يمكن أن تكون القيمة السرّية ذات محتوى إنتروبي عالٍ أيضًا. فمثلًا،
قد تكون سرًّا مشتركًا مشتقًّا من بروتوكول اتّفاق المفاتيح (key agreement)
خاص بـ Diffie-Hellman، أو مفتاح API مكوّنًا من بايتات عشوائيّة تشفيريًّا
(وسنناقش التوليد العشوائي الآمن تشفيريًّا في الفصل التالي). وفي هذه الحالة،
ليس ضروريًّا أن تكون دالة اشتقاق المفاتيح صعبة الحساب: فحتى لو كانت دالة اشتقاق
المفاتيح بسيطة الحساب، فإنّ القيم التي يمكن أن تتّخذها القيمة السرّية كثيرة إلى
حدٍّ ما، فلا يستطيع المهاجم أن يعدّدها كلّها. وسنرى في هذا الفصل أفضل ما في
هذا النوع من دوالّ اشتقاق المفاتيح، وهو HKDF.
قوّة كلمات المرور
TODO: منشور NIST الخاص رقم 800-63
PBKDF2
bcrypt
bcrypt
scrypt
scrypt
HKDF
HKDF، المعرَّف في RFC 5869 rfc5869 والمُفصَّل بدقّة في بحثٍ مرتبط hkdf،
هي دالة اشتقاق مفاتيح مصمَّمة للدخالات ذات الإنترتروبيّة العالية، مثل الأسرار
المشتركة المشتقّة من تبادل مفاتيح Diffie-Hellman. وهي مصمَّمة تحديدًا لا
لتكون آمنة مع الدخالات منخفضة الإنترتروبيّة مثل كلمات المرور.
ويُوجد HKDF ليوفّر على الناس دالة اشتقاق مفاتيح مناسبة جاهزة. وقبل ذلك، كان
اشتقاق المفاتيح في الغالب يجري على نحو ارتجالي لكلّ معيار بعينه. وعادةً ما لم
تكن تلك الحلول الارتجاليّة تملك مقتضيات HKDF الإضافيّة، مثل الملح\s أو معامل
info الاختياري (وسنناقشه لاحقًا في هذا القسم)؛ وذلك في أَجود الحالات فقط، حيث
لم تكن دالة اشتقاق المفاتيح (KDF) معطوبة أصلًا من البداية.
ويُبنى HKDF على HMAC. ومثل HMAC، فإنّه بناء عامّ يستعمل دوالّ التجزئة، ويمكن بناءه باستعمال أيّ دالة تجزئة آمنة تشفيريًّا تشاء.
نظرة أوفى إلى HKDF
متقدّم
يتألّف HKDF من مرحلتين. في المرحلة الأولى، وتُسمّى مرحلة الاستخراج (extraction phase)، يُستخرج مفتاح ثابت الطول من إنتروبيا الدخل. وفي المرحلة الثانية، وتُسمّى مرحلة التوسيع (expansion phase)، يُستعمل ذلك المفتاح لإنتاج عدد من المفاتيح شبه العشوائيّة.
مرحلة الاستخراج '''''''''''''''''''
تقع مرحلة الاستخراج على عاتق استخراج كمٍّ صغير من البيانات ذي محتوى إنتروبي عالٍ من كمٍّ كبير محتمل من البيانات ذو كثافة إنتروبيّة أقلّ.
ولا تستعمل مرحلة الاستخراج إلّا HMAC مع الملح (salt):
def extract(salt, data):
return hmac(salt, data)
وقيمة الملح اختياريّة. فإذا لم يُحدَّد الملح، استُعمل سطر من الأصفار طوله
مساوٍ لطول خرج دالة التجزئة. ومع أنّ الملح اختياريّ من الناحية التقنيّة،
فإنّ المصمّمين يُشدّدون على أهميّته، لأنّه يجعل الاستعمالات المستقلّة لدالة اشتقاق
المفاتيح (مثلًا في تطبيقات مختلفة، أو مع مستخدمين مختلفين) تُنتج نتائج مستقلّة.
وحتّى الملح منخفض الإنترتروبي إلى حدٍّ معقول يمكن أن يُسهم إلى حدٍّ كبير في
أمن دالة اشتقاق المفاتيح. rfc5869 hkdf
وتشرح مرحلة الاستخراج لماذا لا يصلح HKDF لاشتقاق المفاتيح من كلمات المرور.
فبينما مرحلة الاستخراج ممتازة في تركيز الإنترتروبيا، فإنّها غير قادرة على
تكبير الإنترتروبيا. فهي مصمَّمة لضغط كمٍّ صغير من الإنترتروبيا المنتشر على كمٍّ
كبير من البيانات إلى المقدار نفسه من الإنترتروبيا في كمٍّ صغير من البيانات، لكنّها
غير مصمَّمة لإنتاج مجموعة مفاتيح يصعب حسابها عند وجود كمٍّ قليل من الإنترتروبيا
المتاحة. كما أنّه لا مقتضيات تجعل هذه المرحلة مكثفة حسابيًّا. rfc5869
وفي بعض الحالات، يمكن تخطّي مرحلة الاستخراج إذا كان السرّ المشترك يمتلك
الخصائص المطلوبة كلّها، مثلًا إذا كان سطرًا شبه عشوائي بطول كافٍ وبإنتروبيّة
كافية. لكنّ هذا لا ينبغي فعله في بعض الحالات البتّة، مثلًا عند التعامل مع سرّ
مشترك خاص بـ Diffie-Hellman. وتتعمّق RFC قليلًا في مسألة تخطّي هذه الخطوة من
عدمه؛ غير أنّ الأمر غير مُستحسن في العموم. rfc5869
مرحلة التوسيع ''''''''''''''''
في مرحلة التوسيع، تُوسَّع البيانات العشوائيّة المستخرجة من الدخاخل في مرحلة الاستخراج إلى قدر البيانات المطلوب.
وتحخطوة التوسيع بسيطة بدورها: إذ تُنتَج كتل البيانات باستعمال HMAC، وهذه المرّة
بالسرّ المستخرَج لا بالملح العلني الملح، إلى أن يُنتَج عدد كافٍ من البايتات.
والبيانات التي يُطبَّق عليها HMAC هي الخرج السابق (انطلاقًا من سطر فارغ)،
ومعامل «info» (وهو السطر الفارغ أيضًا افتراضيًّا)، وبايت عدّاد يعدّ الكتلة
التي يجري إنتاجها حاليًّا.
def expand(key, info=""):
"""Expands the key, with optional info."""
output = ""
for byte in map(chr, range(256)):
output = hmac(key, output + info + byte)
yield output
def get_output(desired_length, key, info=""):
"""Collects output from the expansion step until enough
has been collected; then returns that output."""
outputs, current_length = [], 0
for output in expand(key, info):
outputs.append(output)
current_length += len(output)
if current_length >= desired_length:
break
else:
# This block is executed when the for loop *isn't*
# terminated by the ``break`` statement, which
# happens when we run out of ``expand`` outputs
# before reaching the desired length.
raise RuntimeError("Desired length too long")
return "".join(outputs)[:desired_length]
ومثل الملح في مرحلة الاستخراج، فإنّ معامل «info» اختياريّ تمامًا، لكنّه يمكن
أن يزيد بدوره إلى حدٍّ كبير في أمن التطبيق. والمقصود بمعامل «info» أن يحتوي على سياق
خاصّ بالتطبيق تُستعمل فيه دالة اشتقاق المفاتيح. ومثل الملح، فإنّه سيجعل دالة
اشتقاق المفاتيح تُنتج قيمًا مختلفة في السياقات المختلفة، ممّا يزيد من أمنها
أكثر. فمثلًا، قد يحتوي معامل info على معلومات عن المستخدم محلّ النظر، أو عن
جزء البروتوكول الذي تُنفَّذ من أجله دالة اشتقاق المفاتيح، أو ما شابه. rfc5869