
مكتبة وحدة أمان الأجهزة بلغة Zig لرموز PIV وCAC وYubiKey عبر PC/SC. تدعم الشهادات وإدارة رقم التعريف الشخصي (PIN) والتوقيع وفك التشفير.
مكتبة وحدة أمان عتادية (HSM) للغة Zig توفر الوصول عبر PC/SC إلى رموز PIV و CAC و YubiKey.
| الجانب | المعلومات |
|---|---|
| استقرار الواجهة البرمجية (API) | قيد التطوير |
| إصدار Zig | 0.16.0 |
| المنصات | لينكس، ماك أو إس، ويندوز |
| الترخيص | MIT |
دعم PIV (NIST SP 800-73-4)
دعم CAC (بطاقة الوصول الشائعة)
دعم YubiKey
الأمان
anyerror)العمليات المتوازية (عبر thread_pool)
-Denable_tp=true)؛ يمكن التعطيل بـ -Denable_tp=falselibpcsclite-dev (ديبيان/أوبونتو) أو pcsc-lite-devel (فيدورا)# ديبيان/أوبونتو
sudo apt-get install libpcsclite-dev pcscd
# فيدورا/RHEL
sudo dnf install pcsc-lite-devel pcsc-lite
# تشغيل خادم PC/SC
sudo systemctl start pcscd
sudo systemctl enable pcscd
# بناء وتشغيل اختبارات الوحدة
make
# أو باستخدام zig مباشرة
zig build test
# تشغيل اختبارات تكامل المحاكي
zig build test -Dintegration_sim=true
# بناء الأمثلة (يتطلب تثبيت مكتبة PC/SC)
zig build examples -Dlink_pcsc=true
مع -Dfips=true، يتم توجيه كل عملية تشفير أساسية تتعلق بالأمان عبر مزود FIPS لـ OpenSSL 3.x المُثبت والمُتحقق منه عبر واجهة crypto_backend، ويكتسب المحاكي عمليات رمز PIV ما بعد الكم (توقيع ML-DSA-65، تغليف/فك تغليف ML-KEM-768) المتوفرة فقط في وضع FIPS. العلم معطل افتراضياً بدون أي عبء إضافي؛ يتم حل تبعية fips فقط تحت -Dfips=true.
# البناء الافتراضي — خلفية std.crypto، لا رابط OpenSSL
zig build test
# بناء FIPS — مزود FIPS لـ OpenSSL (قم بتشغيل `make deps` من fips أولاً)
OPENSSL_CONF=/usr/local/ssl/ssl/openssl.cnf \
zig build test -Dfips=true -Dopenssl_path=/usr/local/ssl
# كلا الوضعين في أمر واحد
make test-dual
انظر docs/FIPS.md لتصميم الواجهة، وسياسة الإعفاء من FIPS، وعمليات رمز PQC.
ملاحظة أمنية: خيار allow_pcsc_env_override معطل افتراضياً لمنع هجمات حقن المكتبات الخبيثة (CWE-427). قم بتمكينه فقط للتطوير/الاختبار:
# بناء الإنتاج (آمن، يتم تجاهل متغير البيئة)
zig build
# بناء التطوير (يسمح بـ HSM_PCSC_LIB_PATH)
zig build -Dallow_pcsc_env_override=true
عند التمكين، يتم التحقق من المكتبات قبل التحميل:
const std = @import("std");
const hsm = @import("hsm");
pub fn main() !void {
var gpa = std.heap.GeneralPurposeAllocator(.{}){};
defer _ = gpa.deinit();
const allocator = gpa.allocator();
// سرد الرموز المتاحة
const tokens = try hsm.listTokens(allocator, false);
defer {
for (tokens) |*t| t.deinit();
allocator.free(tokens);
}
if (tokens.len == 0) {
std.debug.print("لم يتم العثور على أي رموز\n", .{});
return;
}
// فتح الرمز الأول
var token = try hsm.openToken(allocator, tokens[0].id, .{});
defer token.close();
// التحقق من رمز PIN
try token.verifyPin("123456");
// قراءة الشهادة
const cert = try token.getCertificate(.authentication);
defer allocator.free(cert);
std.debug.print("الشهادة: {} بايت\n", .{cert.len});
// توقيع ملخص
var digest: [32]u8 = undefined;
std.crypto.hash.sha2.Sha256.hash("Hello, PIV!", &digest, .{});
const signature = try token.sign(.authentication, .ecdsa_p256, &digest);
defer allocator.free(signature);
std.debug.print("التوقيع: {} بايت\n", .{signature.len});
}
جميع العمليات ترجع أخطاء من HsmError:
PcscUnavailable, ReaderGone, CardRemoved, TimeoutPinIncorrect, PinLocked, PinLengthInvalidNotSupported, SlotNotFound, AlgorithmNotSupportedInvalidTlv, CertificateNotFound, InvalidDigestLengthSecurityConditionNotSatisfied, توفر وحدة parallel عمليات HSM دفعية باستخدام مكتبة thread_pool. تم بناؤها كوحدة Zig منفصلة ومتاحة عندما يكون -Denable_tp=true (الافتراضي). استخدم -Denable_tp=false لإزالة الواجهة بالكامل؛ لا تزال الدوال العامة موجودة وتتراجع إلى تطبيق تسلسلي.
تحت الحد الأدنى، يتم تنفيذ العمليات تسلسلياً دون عبء thread_pool. استدعاءات PKCS#11 الأساسية هي سقالات؛ قم بدمج خلفية PKCS#11 الخاصة بك عن طريق تنفيذ الدوال signData و decryptData و discoverToken و retrieveCert في src/parallel.zig.
# تشغيل جميع اختبارات الوحدة
zig build test
تتضمن المكتبة محاكياً لبطاقة PIV للاختبار بدون عتاد:
# تشغيل اختبارات المحاكي
zig build test -Dintegration_sim=true
يستخدم المحاكي مفاتيح اختبار مضمنة (انظر src/sim/key_material.zig). لا حاجة لملفات مفاتيح خارجية أو متغيرات بيئة.
للاختبار بعتاد حقيقي:
# اختبارات القراءة فقط (قراءة الشهادة)
HSM_HIL=1 zig build test -Dhsm_hil=true
# اختبارات تتطلب رمز PIN
HSM_HIL=1 HSM_PIN=123456 zig build test -Dhsm_hil=true
# اختبارات خطيرة (عمليات الكتابة) - استخدم بحذر
HSM_HIL=1 HSM_PIN=123456 HSM_DANGEROUS=1 zig build test -Dhsm_hil=true
# تخميد مستمر (أوقف يدوياً)
zig build test --fuzz -- --test-filter fuzz
# بناء الأمثلة (يتطلب تثبيت مكتبة PC/SC)
zig build examples -Dlink_pcsc=true
# سرد الرموز
./zig-out/bin/list_tokens
./zig-out/bin/list_tokens --sim # استخدام المحاكي
# التوقيع باستخدام PIV (يتم إدخال PIN عبر موجه TTY تفاعلي)
./zig-out/bin/piv_sign
./zig-out/bin/piv_sign --sim
# أو توفير PIN عبر متغير البيئة (أقل أمناً)
HSM_PIN=123456 ./zig-out/bin/piv_sign --sim
معالجة PIN: لا يتم تخزين رموز PIN أبداً في هياكل الرمز ويتم تصفيرها بعد الاستخدام.
تحليل TLV: جميع تحليلات TLV لها حدود للعمق (10) والطول (64 كيلوبايت) لمنع استنفاد الموارد.
معالجة الأخطاء: تؤدي كلمات الحالة غير المعروفة إلى أخطاء صريحة، وليس فشلاً صامتاً.
التسجيل: لا يتضمن تسجيل التصحيح أبداً بيانات حساسة (رموز PIN، مفاتيح، إلخ).
الذاكرة: يتم تصفير المخازن المؤقتة الحساسة باستخدام hsm.zeroize() الذي يمنع تحسين المترجم.
تحميل مكتبة PC/SC: تقوم المكتبة بتحميل PC/SC من مسارات مطلقة موثوقة. يتوفر تجاوز صريح عبر HSM_PCSC_LIB_PATH (يجب أن يكون مساراً مطلقاً).
يشحن hsm واجهة OpenTelemetry اختيارية لتتبع نطاقات (spans) عمليات HSM.
الواجهة معطلة افتراضياً وتنتج عبئاً صفرياً عند التعطيل — لا يحل البناء تبعية otel مطلقاً، ويتم ترجمة كل دالة مساعدة إلى عملية لا تؤدي شيئاً (no-op) معروفة في وقت الترجمة، ولا تحتوي المنتجات الناتجة على أي رموز otel أو observability مرتبطة.
zig build test # الافتراضي: -Dwith_otel=false (no-op)
zig build test -Dwith_otel=true # اختياري: يتم إصدار النطاقات
عند التمكين، كل استدعاء عام Token.sign / Token.decrypt يصدر نطاقاً hsm.{operation} (مثل hsm.sign، hsm.decrypt) مع سمتين:
crypto.algorithm — علامة الخوارزمية (مثل ecdsa_p256، rsa2048_pkcs1v15).crypto.key.id — معرف الفتحة (مثل authentication، signature، key_management، card_auth).تم تصميم الواجهة بناءً على قاعدة واحدة: عدم تصدير المواد التشفيرية أبداً. يتم تصدير سمات النطاق خارج المضيف (عادةً إلى مجمع OTLP) وتنتهي في التتبعات/السجلات التي قد يكون لديها ضوابط وصول أضعف من عملية HSM نفسها.
hsm.observability.startHsmOperationSpan.const hsm = @import("hsm");
pub fn main() !void {
// ضع قيمة Otel على عنوان مستقر — إما `var` في إطار الدالة main()
// لعمر العملية، أو تخصيص من الكومة. دالة init موجودة فقط عندما
// يكون `-Dwith_otel=true`؛ تحت البناء المعطل،
// يتحول hsm.observability_init إلى بنية فارغة ويتم ترجمة هذه الكتلة
// إلى عملية لا تؤدي شيئاً.
if (comptime hsm.observability.enabled) {
var otel = try hsm.observability_init.Otel.init(allocator, "my-service");
defer otel.deinit();
otel.installGlobals();
}
// ... باقي main، بما في ذلك أي استدعاءات hsm.Token.sign — كل منها
// يصدر الآن نطاق `hsm.sign` مرتبطاً بخدمتك.
}
تقرأ الواجهة متغيرات البيئة OTEL_* (المُعيّن، نقطة نهاية المُصدر، service.name، إلخ) وفقاً لمواصفات متغيرات بيئة OpenTelemetry. الافتراضيات: مُعيّن parentbased_traceidratio بنسبة 5%، مُصدر OTLP/HTTP-protobuf إلى http://localhost:4318.
الوصفة الكاملة للتكامل (توصيلات البناء، منصة الاختبار، أداة التحقق من عدم تنفيذ أي شيء) موثقة في مستودع نشر otel:
otel/docs/integration/RECIPE.md.
يجب اجتياز ثلاثة تحكمات قبل دمج التغييرات التي تمس الواجهة:
zig build test # العلم الافتراضي = false
zig build test -Dwith_otel=true # العلم مفعل
scripts/verify-consumer-noop.sh # التحقق من عدم تسرب الرموز
الفحص verify-consumer-noop.sh يبني المكتبة بـ -Dwith_otel=false ويفحص zig-out/ باستخدام nm --defined-only، ويفشل إذا نجا أي رمز otel/observability في المنتجات. يعكس الفحص otel/scripts/verify-consumer-noop.sh عبر المستودعات (الذي يعمل فقط في تخطيط المستودع المشترك <root>/otel//<root>/hsm/) بحيث يمكن للمساهمين الذين ليس لديهم هذا التخطيط الاستمرار في فحص الواجهة محلياً.
src/
├── hsm.zig # API الرئيسي والأنواع
├── root.zig # نقطة دخول الوحدة
├── apdu.zig # ترميز/فك ترميز APDU
├── tlv.zig # تحليل BER-TLV
├── parallel.zig # العمليات المتوازية (thread_pool)
├── pcsc/
│ └── pcsc.zig # طبقة النقل PC/SC (لينكس/ماك أو إس/ويندوز)
├── piv/
│ └── piv.zig # تنفيذ PIV
├── cac/
│ └── cac.zig # تنفيذ CAC
├── yubikey/
│ └── yubikey.zig # كشف YubiKey
└── sim/
├── sim.zig # نقطة دخول المحاكي
├── transport.zig # النقل المُحاكى
├── piv_card.zig # بطاقة PIV مُحاكاة
└── key_material.zig # مفاتيح اختبار مضمنة
tests/
├── integration.zig # اختبارات التكامل الأساسية
├── sim_integration.zig # اختبارات تكامل المحاكي
└── hil_tests.zig # اختبارات الحلقة العتادية
examples/
├── list_tokens.zig # مثال اكتشاف الرمز
└── piv_sign.zig # مثال التوقيع
انظر CONTRIBUTING.md للإرشادات.
ترخيص MIT - انظر LICENSE للتفاصيل.
بُني باستخدام Zig 0.16.0 | PIV | CAC | YubiKey | PC/SC
| الخيار | الافتراضي | الوصف | التأثير الأمني |
|---|
integration_sim | false | تشغيل اختبارات تكامل المحاكي | لا شيء |
hsm_hil | false | تشغيل اختبارات الحلقة العتادية (يتطلب رمزاً حقيقياً) | لا شيء |
link_pcsc | false | ربط مكتبة PC/SC للأمثلة | لا شيء |
include_simulator | التصحيح: trueالإصدار: false | تضمين محاكي PIV مع مفاتيح اختبار | CWE-321: مفاتيح اختبار في الإنتاج |
allow_pcsc_env_override | false | خطر أمني: السماح بمتغير البيئة HSM_PCSC_LIB_PATH | CWE-427: تحميل مكتبة غير موثوقة |
fips | false | توجيه التشفير الحساس عبر مزود FIPS-140-3 المرتبط (بدون عبء إضافي عند التعطيل) | لا شيء عند التعطيل |
openssl_path | (غير مضبوط) | بادئة تثبيت OpenSSL للتثبيتات غير القياسية (مثل /usr/local/ssl) | لا شيء |
| النوع | الوصف |
|---|
TokenKind | نوع الرمز: .piv, .cac, .yubikey, .unknown |
TokenInfo | بيانات وصفية للرمز المكتشف |
Token | مقبض رمز مفتوح للعمليات |
Slot | فتحة المفتاح: .authentication (9A), .signature (9C), .key_management (9D), .card_auth (9E) |
Algorithm | خوارزمية التشفير: .ecdsa_p256, .ecdsa_p384, .rsa2048_pkcs1v15, إلخ. |
Capabilities | أعلام قدرات الرمز |
PcscScope | نطاق سياق PC/SC: .user (افتراضي), .system |
| الدالة | الوصف |
|---|
listTokens(allocator, use_sim) | سرد الرموز المتاحة |
openToken(allocator, id, opts) | فتح رمز بواسطة المعرف |
token.reconnect() | إعادة الاتصال بالرمز وإعادة تعيين حالة المصادقة |
token.verifyPin(pin) | التحقق من رمز PIN |
token.changePin(old, new) | تغيير رمز PIN |
token.unblockPin(puk, new_pin) | إلغاء حظر رمز PIN باستخدام PUK |
token.getCertificate(slot) | الحصول على الشهادة بصيغة DER |
token.sign(slot, alg, digest) | توقيع ملخص |
token.decrypt(slot, alg, ciphertext) | فك تشفير البيانات |
token.capabilities() | الحصول على قدرات الرمز |
token.close() | إغلاق اتصال الرمز |
AuthenticationFailed| الدالة | الوصف | الحد الأدنى |
|---|
parallelSign(allocator, inputs, results) | توقيع دفعات عبر فتحات متعددة | عنصران أو أكثر |
parallelDecrypt(allocator, inputs, results) | فك تشفير دفعات عبر مفاتيح متعددة | عنصران أو أكثر |
parallelTokenDiscover(readers, results) | اكتشاف الرموز عبر قارئات متعددة | قارئان أو أكثر |
parallelCertRetrieve(allocator, requests, results) | استرجاع الشهادات عبر فتحات متعددة | طلبان أو أكثر |