
Libreria Zig per modulo di sicurezza hardware per token PIV, CAC e YubiKey tramite PC/SC. Supporta certificati, gestione PIN, firma e decifratura.
Una libreria Hardware Security Module (HSM) per Zig che fornisce accesso PC/SC a token PIV, CAC e YubiKey.
| Aspetto | Informazioni |
|---|---|
| Stabilità API | Sviluppo |
| Versione Zig | 0.16.0 |
| Piattaforme | Linux, macOS, Windows |
| Licenza | MIT |
Supporto PIV (NIST SP 800-73-4)
Supporto CAC (Common Access Card)
Supporto YubiKey
Sicurezza
anyerror)Operazioni parallele (tramite thread_pool)
-Denable_tp=true); disabilitare con -Denable_tp=falselibpcsclite-dev (Debian/Ubuntu) o pcsc-lite-devel (Fedora)# Debian/Ubuntu
sudo apt-get install libpcsclite-dev pcscd
# Fedora/RHEL
sudo dnf install pcsc-lite-devel pcsc-lite
# Start the PC/SC daemon
sudo systemctl start pcscd
sudo systemctl enable pcscd
# Build and run unit tests
make
# Or using zig directly
zig build test
# Run simulator integration tests
zig build test -Dintegration_sim=true
# Build examples (requires PC/SC library installed)
zig build examples -Dlink_pcsc=true
Con -Dfips=true, ogni primitiva crittografica rilevante per la sicurezza
viene instradata attraverso un provider FIPS OpenSSL 3.x collegato e validato,
tramite il punto di inserimento crypto_backend, e il simulatore acquisisce
operazioni token PIV post-quantum (firma ML-DSA-65, incapsulamento/disincapsulamento ML-KEM-768)
disponibili solo in modalità FIPS. Il flag è disattivato per impostazione predefinita
con overhead zero; la dipendenza fips viene risolta solo con -Dfips=true.
# Build predefinita — backend std.crypto, nessun collegamento OpenSSL
zig build test
# Build FIPS — provider FIPS OpenSSL (esegui prima `make deps` da fips)
OPENSSL_CONF=/usr/local/ssl/ssl/openssl.cnf \
zig build test -Dfips=true -Dopenssl_path=/usr/local/ssl
# Entrambe le modalità in un colpo solo
make test-dual
Vedi docs/FIPS.md per la progettazione del punto di inserimento,
la politica di esenzione FIPS e le operazioni token PQC.
Nota sulla sicurezza: L'opzione allow_pcsc_env_override è disabilitata per impostazione predefinita per prevenire attacchi di iniezione di librerie dannose (CWE-427). Abilitala solo per sviluppo/test:
# Build di produzione (sicura, variabile d'ambiente ignorata)
zig build
# Build di sviluppo (consente HSM_PCSC_LIB_PATH)
zig build -Dallow_pcsc_env_override=true
Quando abilitate, le librerie vengono validate prima del caricamento:
const std = @import("std");
const hsm = @import("hsm");
pub fn main() !void {
var gpa = std.heap.GeneralPurposeAllocator(.{}){};
defer _ = gpa.deinit();
const allocator = gpa.allocator();
// Elenca i token disponibili
const tokens = try hsm.listTokens(allocator, false);
defer {
for (tokens) |*t| t.deinit();
allocator.free(tokens);
}
if (tokens.len == 0) {
std.debug.print("Nessun token trovato\n", .{});
return;
}
// Apre il primo token
var token = try hsm.openToken(allocator, tokens[0].id, .{});
defer token.close();
// Verifica il PIN
try token.verifyPin("123456");
// Legge il certificato
const cert = try token.getCertificate(.authentication);
defer allocator.free(cert);
std.debug.print("Certificato: {} byte\n", .{cert.len});
// Firma un digest
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("Firma: {} byte\n", .{signature.len});
}
Tutte le operazioni restituiscono errori da HsmError:
PcscUnavailable, ReaderGone, CardRemoved, TimeoutPinIncorrect, PinLocked, PinLengthInvalidNotSupported, SlotNotFound, AlgorithmNotSupportedInvalidTlv, CertificateNotFound, InvalidDigestLengthSecurityConditionNotSatisfied, Il modulo parallel fornisce operazioni HSM batch utilizzando la libreria thread_pool. È costruito come modulo Zig separato e disponibile quando -Denable_tp=true (predefinito). Impostare -Denable_tp=false per compilare completamente il punto di inserimento; le funzioni pubbliche esistono ancora e ricadono su un'implementazione sequenziale.
Sotto la soglia, le operazioni vengono eseguite sequenzialmente senza overhead del thread pool. Le chiamate PKCS#11 sottostanti sono impalcature; integra il tuo backend PKCS#11 implementando le funzioni signData, decryptData, discoverToken e retrieveCert in src/parallel.zig.
# Esegue tutti i test unitari
zig build test
La libreria include un simulatore di carta PIV per test senza hardware:
# Esegue test con simulatore
zig build test -Dintegration_sim=true
Il simulatore utilizza chiavi di test incorporate (vedi src/sim/key_material.zig). Non sono necessari file di chiavi esterni o variabili d'ambiente.
Per test con hardware reale:
# Test di sola lettura (lettura certificati)
HSM_HIL=1 zig build test -Dhsm_hil=true
# Test che richiedono PIN
HSM_HIL=1 HSM_PIN=123456 zig build test -Dhsm_hil=true
# Test pericolosi (operazioni di scrittura) - USARE CON CAUTELA
HSM_HIL=1 HSM_PIN=123456 HSM_DANGEROUS=1 zig build test -Dhsm_hil=true
# Fuzzing continuo (fermare manualmente)
zig build test --fuzz -- --test-filter fuzz
# Compila esempi (richiede libreria PC/SC installata)
zig build examples -Dlink_pcsc=true
# Elenca token
./zig-out/bin/list_tokens
./zig-out/bin/list_tokens --sim # Usa simulatore
# Firma con PIV (PIN inserito tramite prompt TTY interattivo)
./zig-out/bin/piv_sign
./zig-out/bin/piv_sign --sim
# Oppure fornisci PIN tramite variabile d'ambiente (meno sicuro)
HSM_PIN=123456 ./zig-out/bin/piv_sign --sim
Gestione PIN: I PIN non vengono mai memorizzati nelle strutture token e vengono azzerati dopo l'uso.
Parsing TLV: Tutto il parsing TLV ha limiti di profondità (10) e lunghezza (64KB) per prevenire esaurimento delle risorse.
Gestione errori: Parole di stato sconosciute generano errori espliciti, non fallimenti silenziosi.
Logging: Il debug log non include mai dati sensibili (PIN, chiavi, ecc.).
Memoria: I buffer sensibili vengono azzerati utilizzando hsm.zeroize() che impedisce l'ottimizzazione del compilatore.
Caricamento libreria PC/SC: La libreria carica PC/SC da percorsi assoluti fidati. È disponibile un override esplicito tramite HSM_PCSC_LIB_PATH (deve essere assoluto).
hsm include un punto di inserimento OpenTelemetry opzionale per il tracciamento degli span delle operazioni HSM.
Il punto di inserimento è disattivato per impostazione predefinita e produce overhead zero
quando disattivato — la build non risolve mai la dipendenza otel, ogni
helper si compila in un no-op noto in fase di compilazione, e gli artefatti prodotti
non contengono simboli otel o observability collegati.
zig build test # predefinito: -Dwith_otel=false (no-op)
zig build test -Dwith_otel=true # opt-in: span emessi
Quando abilitato, ogni chiamata pubblica Token.sign / Token.decrypt emette
uno span hsm.{operazione} (es. hsm.sign, hsm.decrypt) con due
attributi:
crypto.algorithm — tag algoritmo (es. ecdsa_p256,
rsa2048_pkcs1v15).crypto.key.id — identificatore slot (es. authentication,
signature, key_management, card_auth).Il punto di inserimento è progettato attorno a una regola: MAI esportare materiale crittografico. Gli attributi dello span vengono esportati fuori dall'host (tipicamente verso un collector OTLP) e finiscono in trace/log che potrebbero avere controlli di accesso più deboli rispetto all'operazione HSM stessa.
hsm.observability.startHsmOperationSpan.const hsm = @import("hsm");
pub fn main() !void {
// Posiziona il valore Otel su un indirizzo stabile — sia una variabile nello
// stack frame di main() per tutta la durata del processo, sia un'allocazione
// heap. L'helper di init è presente solo quando `-Dwith_otel=true`;
// nella build disabilitata, hsm.observability_init si risolve in una struct vuota
// e questo blocco si compila in un no-op.
if (comptime hsm.observability.enabled) {
var otel = try hsm.observability_init.Otel.init(allocator, "my-service");
defer otel.deinit();
otel.installGlobals();
}
// ... resto di main, incluse eventuali chiamate hsm.Token.sign — ciascuna
// ora emette uno span `hsm.sign` allegato al tuo servizio.
}
Il punto di inserimento legge le variabili d'ambiente OTEL_* (sampler, exporter
endpoint, service.name, ecc.) secondo la specifica delle variabili d'ambiente OpenTelemetry.
Predefiniti: sampler parentbased_traceidratio al 5%, exporter OTLP/HTTP-protobuf
verso http://localhost:4318.
La ricetta di integrazione completa (cablaggio build, test harness, verificatore no-op)
è documentata nel repository di rollout otel:
otel/docs/integration/RECIPE.md.
Tre porte devono superare prima di unire modifiche che toccano il punto di inserimento:
zig build test # flag predefinito = false
zig build test -Dwith_otel=true # flag attivo
scripts/verify-consumer-noop.sh # controllo perdita simboli
La porta verify-consumer-noop.sh compila la libreria con
-Dwith_otel=false e analizza zig-out/ con nm --defined-only,
fallendo se qualche simbolo otel/observability sopravvive negli
artefatti. Rispecchia lo script cross-repo otel/scripts/verify-consumer-noop.sh (che viene eseguito solo nella disposizione repo fratello
<radice>/otel//<radice>/hsm/) in modo che i contributori senza quella disposizione
possano comunque verificare il punto di inserimento localmente.
src/
├── hsm.zig # API principale e tipi
├── root.zig # Punto di ingresso modulo
├── apdu.zig # Codifica/decodifica APDU
├── tlv.zig # Parsing BER-TLV
├── parallel.zig # Operazioni parallele (thread_pool)
├── pcsc/
│ └── pcsc.zig # Livello trasporto PC/SC (Linux/macOS/Windows)
├── piv/
│ └── piv.zig # Implementazione PIV
├── cac/
│ └── cac.zig # Implementazione CAC
├── yubikey/
│ └── yubikey.zig # Rilevamento YubiKey
└── sim/
├── sim.zig # Punto di ingresso simulatore
├── transport.zig # Trasporto simulato
├── piv_card.zig # Scheda PIV simulata
└── key_material.zig # Chiavi di test incorporate
tests/
├── integration.zig # Test di integrazione base
├── sim_integration.zig # Test di integrazione simulatore
└── hil_tests.zig # Test hardware-in-loop
examples/
├── list_tokens.zig # Esempio di scoperta token
└── piv_sign.zig # Esempio di firma
Vedi CONTRIBUTING.md per le linee guida.
Licenza MIT - vedi LICENSE per i dettagli.
Costruito con Zig 0.16.0 | PIV | CAC | YubiKey | PC/SC
| Opzione | Predefinito | Descrizione | Impatto sulla sicurezza |
|---|
integration_sim | false | Esegui test di integrazione con simulatore | Nessuno |
hsm_hil | false | Esegui test hardware-in-loop (richiede token reale) | Nessuno |
link_pcsc | false | Collega libreria PC/SC per esempi | Nessuno |
include_simulator | Debug: trueRelease: false | Includi simulatore PIV con chiavi di test | CWE-321: Chiavi di test in produzione |
allow_pcsc_env_override | false | RISCHIO PER LA SICUREZZA: consenti variabile d'ambiente HSM_PCSC_LIB_PATH | CWE-427: Caricamento libreria non fidato |
fips | false | Instrada crittografia di sicurezza attraverso il provider FIPS-140-3 collegato (zero overhead quando disattivato) | Nessuno quando disattivato |
openssl_path | (non impostato) | Prefisso di installazione OpenSSL per installazioni non standard (es. /usr/local/ssl) | Nessuno |
| Tipo | Descrizione |
|---|
TokenKind | Tipo di token: .piv, .cac, .yubikey, .unknown |
TokenInfo | Metadati del token scoperto |
Token | Handle token aperto per operazioni |
Slot | Slot chiave: .authentication (9A), .signature (9C), .key_management (9D), .card_auth (9E) |
Algorithm | Algoritmo crittografico: .ecdsa_p256, .ecdsa_p384, .rsa2048_pkcs1v15, ecc. |
Capabilities | Flag delle capacità del token |
PcscScope | Ambito del contesto PC/SC: .user (predefinito), .system |
| Funzione | Descrizione |
|---|
listTokens(allocator, use_sim) | Elenca i token disponibili |
openToken(allocator, id, opts) | Apre un token tramite ID |
token.reconnect() | Riconnette al token e resetta lo stato di autenticazione |
token.verifyPin(pin) | Verifica il PIN |
token.changePin(old, new) | Cambia il PIN |
token.unblockPin(puk, new_pin) | Sblocca il PIN con PUK |
token.getCertificate(slot) | Ottiene il certificato in formato DER |
token.sign(slot, alg, digest) | Firma un digest |
token.decrypt(slot, alg, ciphertext) | Decifra dati |
token.capabilities() | Ottiene le capacità del token |
token.close() | Chiude la connessione al token |
AuthenticationFailed| Funzione | Descrizione | Soglia |
|---|
parallelSign(allocator, inputs, results) | Firma batch su più slot | 2+ elementi |
parallelDecrypt(allocator, inputs, results) | Decifratura batch su più chiavi | 2+ elementi |
parallelTokenDiscover(readers, results) | Scoperta token tra lettori | 2+ lettori |
parallelCertRetrieve(allocator, requests, results) | Recupero certificati tra slot | 2+ richieste |