Skip to content

Repository files navigation

HeimdallGK

A device authentication / access-control gatekeeper: a heimdall-agent enrolls a device (hardware-derived identity + a real hybrid Ed25519 + ML-DSA-65 keypair), an administrator approves it, and it then authenticates to heimdall-server via signed challenge-response over a small binary protocol. Name: Heimdall, the Norse watchman guarding the Bifrost, + GK = GateKeeper.

Trust model in one sentence

The only accepted security weakness is the admin's PIN-protected recovery key (meant to live on a USB drive, physically held by whoever administers the system) - everything else (a shell on the server, a copy of the database, a stolen agent laptop) is designed to not be a shortcut around it. See docs/threat-model.md for the full reasoning.

What's real here

  • TLS 1.3 on the wire, trust-on-first-use certificate pinning (crypto/tls.c) - live-tested handshake, first-contact pinning, and rejection of a changed/MITM certificate.
  • RBAC with multiple named admins (AUDITOR/OPERATOR/ SUPER_ADMIN, server/src/admin_auth.c, backed by SQLite): admin authority is not "whoever has a shell here" - every approve/ block requires proving possession of a specific, named admin's PIN-protected Ed25519 recovery key. First run walks you through generating the first one - point it at a USB drive, not the server's own disk.
  • Software HSM (crypto/hsm.c: PBKDF2-HMAC-SHA256 + AES-256-GCM at rest, key material wiped from memory right after use). A reserved, unimplemented slot exists for a real hardware HSM/PKCS#11 key once hardware is available to test against.
  • Structured audit logging, SIEM-ready (server/src/audit.c): every enroll/approve/block/auth event as JSON, optionally forwarded live via RFC 5424 syslog to a SIEM collector (HGK_SYSLOG_HOST).
  • Optional AD/LDAP bind (server/src/ldap_auth.c, HGK_LDAP_URI) as a third factor on top of the recovery key - never a replacement for it.
  • Cryptography: SHA-256 and Ed25519 via OpenSSL, Dilithium/ML-DSA via liboqs, and a Hybrid mode combining both - not placeholders. See tests/test_crypto.c, which also proves tampered signatures and wrong-key signatures are rejected.
  • Agent identity at rest is encrypted, not a plaintext file: Windows DPAPI (machine-scoped), Linux AES-256-GCM keyed off /etc/machine-id (weaker - see the threat model).
  • Persistence: SQLite (server/src/database.c), one row per device.
  • Replay protection: a session's challenge is single-use and time-limited (server/src/session.c, tests/test_replay.c).
  • Hardware fingerprint: real OS-reported identifiers (Windows: registry MachineGuid + computer name + volume serial; Linux: /etc/machine-id + hostname) - not a hardcoded string, verified deterministic and machine-distinct across both platforms. Bound to every re-authentication, not just enrollment: AUTH_RESPONSE now carries the current fingerprint and server/src/verify.c denies a mismatch, so a cloned device_id+private key replayed from different hardware is rejected (tests/test_hardware_binding.c).
  • TPM: real detection on Windows via the TBS API. Hardware-backed device key generation/signing is not implemented (see docs/threat-model.md); the agent falls back to its software keypair, and says so out loud.
  • End-to-end flow: enroll -> admin approval (recovery-key authorized) -> re-authenticate -> access granted, using persisted agent identity, has been run and verified manually - see examples/minimal_enroll_auth.

What isn't (yet)

  • No cross-session channel binding for the app-layer challenge - see the threat model for why this matters much less now that TLS prevents network-level replay/MITM.
  • No real hardware HSM/PKCS#11 support for admin keys yet (HGK_HSM_PKCS11 in crypto/hsm.c is a reserved, unimplemented slot - no hardware was available here to test against).
  • No formal certification (FIPS 140 validated module, SOC 2 audit, Common Criteria) - see docs/compliance.md for what's already aligned versus what needs an accredited lab/auditor.
  • No HA/clustering - single server process, local SQLite.
  • HGK_MSG_REVOKE is declared but unhandled.

Full details in docs/threat-model.md and docs/compliance.md.

Building

Requires a C11 compiler, CMake 3.20+, Ninja, OpenSSL, liboqs, and SQLite3 from the same toolchain family (mixing runtimes causes linker errors on Windows/MinGW). On Windows, the recommended source for all of it is MSYS2's ucrt64 environment:

pacman -S mingw-w64-ucrt-x86_64-gcc \
          mingw-w64-ucrt-x86_64-openssl \
          mingw-w64-ucrt-x86_64-liboqs \
          mingw-w64-ucrt-x86_64-sqlite3 \
          mingw-w64-ucrt-x86_64-openldap

Then configure and build:

cmake -B build -G Ninja \
  -DCMAKE_C_COMPILER=<msys2>/ucrt64/bin/gcc.exe \
  -DCMAKE_PREFIX_PATH=<msys2>/ucrt64
cmake --build build

On Linux, install the equivalent -dev packages for OpenSSL, liboqs, and SQLite3 through your distro's package manager and drop -DCMAKE_C_COMPILER/-DCMAKE_PREFIX_PATH.

On Windows, copy the runtime DLLs next to the built executables before running them:

cp <msys2>/ucrt64/bin/{libssl-3-x64,libcrypto-3-x64,liboqs-9,libsqlite3-0,libldap,liblber,libsasl2-3}.dll build/

Running tests

cd build
ctest --output-on-failure

Fuzz testing

Two harnesses (tests/fuzz/) target the wire-protocol decoder and the full packet handler. They're built by default (fuzz_protocol_decode/fuzz_handle_packet next to the other binaries) but not wired into ctest, since they run millions of iterations by design:

cd build
./fuzz_protocol_decode              # ~3M iterations by default
HGK_FUZZ_ITERATIONS=100000 ./fuzz_handle_packet

Run so far without real sanitizers (this project's MinGW/Windows toolchain has no working ASan/libFuzzer runtime): 5,000,000 iterations of fuzz_protocol_decode and 30,000 of fuzz_handle_packet, zero crashes. Run again on native Linux (Ubuntu, clang, real libFuzzer + AddressSanitizer + UBSan this time): 17,245,432 iterations of fuzz_protocol_decode (~142k exec/s) and 18,289 of fuzz_handle_packet (slower - full enroll/registry/ SQLite path per iteration), zero crashes, zero sanitizer reports in both. See docs/fuzzing.md for the full breakdown and exact commands.

Running the full flow

See examples/minimal_enroll_auth.

Layout

  • agent/ - client: hardware identity, keypair, TLS connection state machine.
  • server/ - device registry, SQLite persistence, session/handler, RBAC admin console (admin.c/admin_auth.c), audit logging (audit.c), optional LDAP (ldap_auth.c).
  • common/ - the shared wire protocol.
  • crypto/ - the crypto backend (heimdall-crypto static library): classical/PQC signing (crypto.c, ed25519.c, dilithium.c), the software HSM (hsm.c), TLS (tls.c), CSPRNG (include/random.c).
  • docs/ - architecture, protocol spec, threat model, compliance, fuzzing.
  • tests/ - unit tests (ctest) and fuzz harnesses (tests/fuzz/).
  • examples/ - end-to-end walkthroughs.
  • sdk/ - reserved; no third-party client library exists yet, scope not decided.

Security

Found a vulnerability? See SECURITY.md. The security model/tradeoffs are documented in docs/threat-model.md, and enterprise/ compliance readiness in docs/compliance.md.

Version

Current: 1.1.2 (heimdall-agent --version / heimdall-server --version). See CHANGELOG.md.

License

MIT - see LICENSE.

About

Gatekeeper respect it hahaha

Resources

Security policy

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages