التشفير في Rust: التشفير والتجزئة باستخدام مكتبات ring و sha2
Rust هي لغة برمجة أنظمة تضمن أمان الذاكرة وأداءً عاليًا. بفضل هذه الصفات، فهي مثالية لمهام التشفير. في هذه المقالة، سنستعرض مكتبتين شائعتين: ring (للتشفير المتماثل والتجزئة) و sha2 (للتجزئة البحتة). ستتعلم كيفية تشفير البيانات باستخدام AES-GCM، وحساب تجزئات SHA-256، وإنشاء توقيعات HMAC.
لماذا Rust للتشفير؟
تزيل Rust فئات كاملة من الثغرات الأمنية (تجاوز سعة المخزن المؤقت، سباقات البيانات) في مرحلة الترجمة. مكتبات التشفير في Rust، مثل ring، مكتوبة بلغة التجميع وC مع أغلفة آمنة، مما يوفر سرعة مماثلة لـ C ولكن بموثوقية Rust.
إعداد المشروع
أنشئ مشروعًا جديدًا وأضف التبعيات في Cargo.toml:
[dependencies]ring = "0.17"sha2 = "0.10"hex = "0.4" # لعرض البايتات بسهولةالتشفير المتماثل: AES-GCM مع ring
AES-GCM (وضع Galois/Counter) هو معيار حديث للتشفير المتماثل يوفر السرية والمصادقة (التحقق من السلامة). توفر مكتبة ring واجهة برمجة تطبيقات بسيطة وآمنة.
تشفير البيانات
للتشفير، تحتاج إلى: مفتاح (256 بت)، nonce (96 بت)، ونص عادي. يجب أن يكون nonce فريدًا لكل عملية تشفير بنفس المفتاح.
use ring::aead::{Aad, LessSafeKey, Nonce, UnboundKey, AES_256_GCM};use ring::rand::{SecureRandom, SystemRandom};
fn encrypt_aes_gcm(key: &[u8; 32], plaintext: &[u8]) -> Vec<u8> { let rng = SystemRandom::new(); let unbound_key = UnboundKey::new(&AES_256_GCM, key).unwrap(); let key = LessSafeKey::new(unbound_key);
// توليد nonce عشوائي let mut nonce_bytes = [0u8; 12]; rng.fill(&mut nonce_bytes).unwrap(); let nonce = Nonce::assume_unique_for_key(nonce_bytes);
// التشفير مع بيانات إضافية فارغة (AAD) let mut in_out = plaintext.to_vec(); let tag = key.seal_in_place_separate_tag(nonce, Aad::empty(), &mut in_out).unwrap(); // إرجاع nonce + البيانات المشفرة + علامة المصادقة let mut result = nonce_bytes.to_vec(); result.extend_from_slice(&in_out); result.extend_from_slice(tag.as_ref()); result}فك التشفير
لفك التشفير، نستخرج nonce والعلامة والنص المشفر، ثم نستدعي open_in_place.
fn decrypt_aes_gcm(key: &[u8; 32], ciphertext: &[u8]) -> Option<Vec<u8>> { if ciphertext.len() < 28 { return None; } // 12 (nonce) + 16 (علامة) كحد أدنى
let (nonce_bytes, rest) = ciphertext.split_at(12); let (encrypted, tag_bytes) = rest.split_at(rest.len() - 16); let nonce = Nonce::assume_unique_for_key(*nonce_bytes); let unbound_key = UnboundKey::new(&AES_256_GCM, key).unwrap(); let key = LessSafeKey::new(unbound_key);
let mut in_out = encrypted.to_vec(); in_out.extend_from_slice(tag_bytes); // ring يتوقع العلامة في النهاية match key.open_in_place(nonce, Aad::empty(), &mut in_out) { Ok(plaintext) => { let plain_len = plaintext.len(); Some(plaintext[..plain_len].to_vec()) }, Err(_) => None // مفتاح خاطئ أو بيانات تالفة }}مثال استخدام
fn main() { let key = b"0123456789abcdef0123456789abcdef"; // 32 بايت let data = b"رسالة سرية لـ Rust!"; let encrypted = encrypt_aes_gcm(key, data); println!("مشفر (hex): {}", hex::encode(&encrypted)); if let Some(decrypted) = decrypt_aes_gcm(key, &encrypted) { println!("مفكوك التشفير: {}", String::from_utf8(decrypted).unwrap())