محرك التصديق (الصدأ)
يتحقق محرك التصديق من سلامة البرامج الثابتة لـ CPS/IoT باستخدام التوقيعات الرقمية ECDSA P-256. يقوم كل جهاز طرفي بشكل دوري بتوقيع حمولة تثبت عدم التلاعب بالبرامج الثابتة الخاصة به.
لماذا شهادة الأجهزة؟
في البيئة السيبرانية المادية، يمكن أن تتسبب البرامج الثابتة المخترقة في حدوث ضرر جسدي. على عكس أصول تكنولوجيا المعلومات التي يمكنك ببساطة إعادة تصويرها، فإن جهاز التحكم المنطقي القابل للبرمجة (PLC) الذي تم العبث به والذي يتحكم في محطة معالجة المياه يمكن أن يسمم إمدادات المياه.
توفر الشهادة دليلاً رياضيًا على أن البرنامج الثابت الذي يعمل على الجهاز يطابق الإصدار المعروف.
تدفق التصديق
نموذج الطلب/الاستجابة
طلب
#[derive(Deserialize)]
pub struct AttestationVerifyRequest {
pub device_id: String,
pub firmware_hash: String, // SHA-256 hex of firmware binary
pub boot_count: u64, // Monotonic counter (never decreases)
pub signature_hex: String, // ECDSA P-256 signature in hex
pub public_key_hex: Option<String>, // Optional: for initial enrollment
pub nonce: Option<String>, // Challenge-response nonce
}
إجابة
#[derive(Serialize)]
pub struct AttestationVerifyResponse {
pub valid: bool,
pub device_id: String,
pub status: String, // "valid" | "failed" | "structural_pass"
pub reason: String, // Detailed explanation
}
عملية التحقق
الخطوة 1: بناء الرسالة
// The signed message is a concatenation of identity fields
let message = format!(
"{}{}{}{}",
req.device_id,
req.firmware_hash,
req.boot_count,
req.nonce.as_deref().unwrap_or("")
);
الخطوة 2: ملخص SHA-256
use sha2::{Sha256, Digest};
let digest = Sha256::digest(message.as_bytes());
الخطوة 3: القرار الرئيسي
// Priority order for public key:
// 1. Request-provided key (initial enrollment)
// 2. Static device registry (compile-time known devices)
// 3. HashiCorp Vault lookup (production)
let public_key = req.public_key_hex.as_deref()
.or_else(|| DEVICE_KEYS.get(req.device_id.as_str()).copied());
الخطوة 4: التحقق من ECDSA
use p256::ecdsa::{signature::Verifier, Signature, VerifyingKey};
let sig_bytes = hex::decode(&req.signature_hex)?;
let signature = Signature::from_der(&sig_bytes)?;
let key_bytes = hex::decode(public_key)?;
let verifying_key = VerifyingKey::from_sec1_bytes(&key_bytes)?;
match verifying_key.verify(&digest, &signature) {
Ok(_) => {
// Signature valid — firmware is authentic
redis.publish_attestation(&req.device_id, "valid", "Signature verified").await;
Json(AttestationVerifyResponse {
valid: true,
status: "valid".into(),
reason: "ECDSA P-256 signature verified successfully".into(),
// ...
})
}
Err(e) => {
// Signature invalid — potential tampering!
redis.publish_attestation(&req.device_id, "failed", &e.to_string()).await;
Json(AttestationVerifyResponse {
valid: false,
status: "failed".into(),
reason: format!("Signature verification failed: {}", e),
// ...
})
}
}
الخطوة 5: التراجع الهيكلي
في حالة عدم توفر مفتاح عام (تطوير أو أجهزة غير مسجلة)، يقوم المحرك بإجراء التحقق الهيكلي:
// No key available — fallback to structural check
let structural_valid = !req.firmware_hash.is_empty()
&& req.firmware_hash.len() == 64
&& req.boot_count > 0;
if structural_valid {
// Accept with lower confidence
Json(AttestationVerifyResponse {
valid: true,
status: "structural_pass".into(),
reason: "No public key — structural validation only".into(),
})
}
لماذا ECDSA P-256؟
| خوارزمية | حجم المفتاح | حجم التوقيع | سرعة | دعم الأجهزة |
|---|---|---|---|---|
| آر إس إيه-2048 | 256 بايت | 256 بايت | بطيء | محدود |
| ECDSA P-256 | 32 بايت | 64 بايت | سريع | ** STM32 PKA، nRF CC310 ** |
| إد25519 | 32 بايت | 64 بايت | الأسرع | نادر في وحدات MCU |
تم اختيار ECDSA P-256 للأسباب التالية:
- تسريع الأجهزة — يتمتع كل من STM32's PKA وnRF52's CryptoCell CC310 بدعم الأجهزة P-256
- التوقيعات الصغيرة — يتم وضع 64 بايت في حزمة MQTT واحدة بكفاءة
- الامتثال للمعايير — معتمد من NIST، ويتطلبه العديد من المعايير الصناعية
- تخزين المفاتيح — تتلاءم المفاتيح الخاصة بسعة 32 بايت مع مناطق ذاكرة MCU OTP/UICR
حماية عدد التمهيد
يمنع عدد التمهيد الرتيب هجمات إعادة التشغيل:
Attack scenario without boot_count:
1. Attacker captures valid attestation message
2. Replays it after installing malicious firmware
3. System accepts the stale (but valid) signature
With boot_count:
1. Each boot increments a hardware counter stored in OTP
2. Counter can never decrease (hardware fuse)
3. Replayed message has stale boot_count → rejected
التكامل مع Vault PKI
في مرحلة الإنتاج، تتم إدارة المفاتيح العامة للجهاز بواسطة HashiCorp Vault:
تكوين المخزن:
- محرك PKI: يصدر شهادات جهاز ECDSA P-256 (صلاحية لمدة عام واحد)
- محرك النقل: يخزن مفاتيح توقيع الجهاز للتحقق منها
- السياسة: دور
aurorasoc-devices— EC P-256، مصادقة العميل EKU
ريديس للنشر
يتم نشر نتائج التصديق على دفق aurora:cps:attestation:
pub async fn publish_attestation(
&self, device_id: &str, status: &str, reason: &str
) {
let mut conn = self.pool.get().await?;
redis::cmd("XADD")
.arg("aurora:cps:attestation")
.arg("MAXLEN").arg("~").arg("10000")
.arg("*")
.arg("device_id").arg(device_id)
.arg("status").arg(status)
.arg("reason").arg(reason)
.arg("timestamp").arg(chrono::Utc::now().to_rfc3339())
.query_async(&mut conn)
.await?;
}