Security Low-Level Design (LLD)
This document defines the low-level security design for Mutant's bytecode generation, signing, transport, loading, verification, runtime execution, anti-debugging, integrity monitoring, and telemetry…
1. Document Purpose#
This document defines the low-level security design for Mutant's bytecode generation, signing, transport, loading, verification, runtime execution, anti-debugging, integrity monitoring, and telemetry paths.
It is implementation-accurate to the current codebase and intended for:
- Security engineering and code reviews
- Incident response and operations enablement
- Regression prevention for future security changes
- Test strategy and CI policy enforcement
This LLD focuses on anti-tamper and anti-piracy controls under an offline-first threat model.
Companion deep dives:
2. Security Objectives#
2.1 Primary Objectives#
- Ensure tampered artifacts are detected before or during execution.
- Ensure secure mode is fail-closed by default.
- Protect payload confidentiality and integrity at rest.
- Reduce effectiveness of dynamic analysis/debugging.
- Detect runtime instruction tampering and respond per policy.
- Provide observable security signals via telemetry and audit output.
2.2 Non-Objectives#
- Absolute malware-grade anti-debug resistance (not possible in pure userland).
- Hardware-rooted trust chain (not implemented yet).
- Remote attestation and centralized key management (not implemented yet).
3. Threat Model#
3.1 In Scope Adversary Capabilities#
- Reads and modifies
.muartifacts on disk. - Attempts re-signing with attacker-generated keypairs.
- Runs runtime under debugger/instrumentation.
- Patches bytecode/in-memory instruction regions.
- Replays older artifacts or malformed payloads.
- Manipulates environment variables at launch.
3.2 Out of Scope (Current Release)#
- Kernel-level adversaries with arbitrary memory write.
- Hypervisor-level introspection attacks.
- Physical attacks against hardware secrets.
4. Security Modes and Policy Semantics#
Mutant currently has three practical launch postures:
- Secure mode (
--secure, default):
- Runtime posture defaults to secure execution gates.
- Trusted signer pinning is enforced only when
--signer-authis enabled. - Default tamper response is
terminate. - If trusted key env is not set, runtime bootstraps a local persistent keypair and uses the local public key as trusted key for signer-auth verification.
- Compatibility mode (
--compat):
- Signature verification uses embedded signer key (format validity only).
- Default tamper response is
warn.
- Developer mode (
--dev):
- Runtime behavior is forced to compatibility mode.
- If no runtime password is supplied for
.mu, default deterministic local password fallback is used (mutil.GetPwd()). - Intended for local testing convenience, not production hardening.
4.1 Mode Resolution Rules#
CLI precedence is "last matching mode flag wins" due to linear arg scanning.
- Encounter
--compator--dev-> mode becomes compatibility. - Encounter
--securelater -> mode becomes secure again.
4.2 Tamper Response Policy#
Policy source: env variable MUTANT_TAMPER_RESPONSE.
Valid values:
warn: log and continuedelay: sleep then continueterminate: return error/fail
Defaults:
- secure mode:
terminate - compatibility/dev mode:
warn
Delay tuning:
MUTANT_TAMPER_DELAY_MSin[0..5000]- fallback default:
250ms
4.3 Protection Profiles#
Mutant also supports a protection profile layer via MUTANT_PROTECTION_PROFILE.
Supported values:
minimal
- Favors compatibility and operator convenience.
- Defaults tamper policy to
warn. - Defaults risky builtin groups to allow-all unless an explicit capability list is set.
standard
- Default profile when the env var is unset or invalid.
- Keeps secure mode fail-closed and compatibility mode warn-by-default.
paranoid
- Favors maximum tamper resistance.
- Defaults tamper policy to
terminatewhen this profile is selected. - Explicit
MUTANT_TAMPER_RESPONSEstill has higher precedence.
Profile precedence:
- Explicit
MUTANT_TAMPER_RESPONSEstill wins when set. - Explicit
MUTANT_BUILTIN_CAPABILITIESstill wins when set. - Profile selection only controls defaults.
5. Component Architecture#
5.1 Security-Critical Modules#
security/crypto.go
- AES-GCM payload encryption/decryption
- metadata serialization/parsing
security/kdf.go
- Argon2id derivation and parameter validation
- deterministic HKDF utilities
security/signing.go+security/signatures.go
- Ed25519 signatures
- signed artifact envelope parse/verify
- trusted key pinning
security/secure_random.go
- ChaCha20 offset-aware stream masking
- secure compare/zeroing helpers
security/response_policy.go
- central tamper response decisions
security/telemetry.go
- counters, JSON snapshot/export, audit log
security/antidebug_*.go
- platform-specific debugger detection
runner/runner.go
- execution gate and staged enforcement
vm/vm.go
- runtime decode path + integrity probes + policy integration
code/code.go
- offset-aware operand reads
object/secure_memory.go
- secure wrappers and object offset conventions
6. Artifact Format and Cryptographic Construction#
6.1 Signed Artifact Envelope (`.mu`)#
Outer format:
HEADER |-| ENCODED_DATA |-| SIGNATURE_HEX |-| PUBLIC_KEY_HEX |-| FOOTER
Constants:
HEADER = MUTFOOTER = ANTOUTER_SEPERATOR = |-|
Notes:
- Parser validates minimum parts and header/footer sentinels.
- Signature/public key are hex encoded.
ENCODED_DATAis the serialized encryption metadata string.
6.1.1 Standalone Release Trailer#
Release binaries produced by the generator append a standalone trailer after the embedded runtime payload.
Current format version: V3
MUTANTBC | version | payload_len | payload_sha256 | canary | profile_code | provenance_sha256
Field details:
version
- V1 and V2 are accepted for legacy compatibility.
- V3 is the current emission format.
payload_len
- Big-endian
uint64payload length.
payload_sha256
- SHA-256 over the embedded bytecode payload.
canary
- Short SHA-256 derived guard used to detect trailer corruption.
profile_code
- Encoded protection profile selected at build time.
1 = minimal,2 = standard,3 = paranoid.
provenance_sha256
- SHA-256 over
payload || payload_sha256 || profile_code. - Used to detect tampering and mismatched release provenance.
Compatibility rules:
- Runner validates V3 first, then V2, then V1.
- V1 and V2 remain supported for older artifacts.
- V3 is required for new release builds.
6.2 Encrypted Metadata Payload#
ENCODED_DATA format in password mode:
ciphertext_b64 | salt_hex | <empty-sourcehash> | true | iter | memory | threads
Deterministic mode variant stores sourceHash and false.
Constants:
- inner separator:
SEPERATOR = | - AEAD associated data:
ENCSIG = MUTANT
6.3 Encryption Layers#
Compilation encryption layering (current implementation):
- VM bytecode and constants are stream-masked (
mutil.EncryptByteCode,SecureXOR). - Full gob byte slice is XOR-wrapped with embedded random key
(
SecureXOREncrypt). - Result is AES-GCM encrypted using Argon2id-derived key (
AESEncrypt). - Encrypted metadata is signed with Ed25519 (
SignCode).
Rationale:
- AES-GCM gives confidentiality + integrity for payload.
- Signature protects authenticity and allows trusted signer pinning in secure mode.
- Additional stream/XOR layers increase analysis friction.
The standalone release trailer adds a separate provenance check path so the runtime can validate the generated build profile and trailer integrity before payload decode.
7. Key Derivation and Password Handling#
7.1 Password KDF (Argon2id)#
Derivation path:
DeriveKeyFromPassword(password, salt)- defaults:
- time: 1
- memory: 64 MB (
64*1024KB) - threads: 4
- key length: 32 bytes
Validation hard bounds:
- time:
[1..8] - memory:
[64MB..4GB]in KB units - threads:
[1..16]
7.2 Password Quality Policy#
Easy memorable passwords
7.3 Build-Time Profile Binding#
Release generation binds the selected protection profile into the standalone
trailer via the profile_code field.
Security implication:
- The build mode becomes auditable in the emitted artifact.
- Trailer provenance mismatch is treated as tamper.
- Runtime behavior still honors explicit environment overrides for policy controls.
7.4 Deterministic Local Password Fallback#
mutil.GetPwd() uses HKDF-SHA512 over fixed context constants to produce a
deterministic local key string.
Used when:
- Single-arg
.mut/.muconvenience paths. --devmode running.muwithout explicit-pwd.
Security implication:
- deterministic and recoverable from binary/source; convenience only.
- not a production secret.
8. Signing and Trust Model#
8.1 Signature Primitive#
- Algorithm: Ed25519
- Signing input: SHA-256 hash of
ENCODED_DATA
8.2 Verification Paths#
- Compatibility verification (
VerifyCode):
- Parses embedded public key and signature.
- Verifies signature correctness for envelope integrity.
- Does not enforce signer identity trust.
- Secure verification (
VerifyCodeWithTrustedPublicKey):
- Parses embedded public key/signature.
- Loads trusted key from env
MUTANT_TRUSTED_PUBLIC_KEY_HEXwhen set. - Falls back to local key bootstrap resolution when env is absent.
- Constant-time compares embedded key with trusted key.
- On mismatch returns
ErrUntrustedSigner. - Verifies signature using trusted key.
8.3 Signing Key Injection#
Build/signing key source:
MUTANT_SIGNING_PRIVATE_KEY_HEXpreferred for stable signer identity.- If absent, local persistent keypair is loaded or bootstrapped from local keystore.
Operational risk:
- unmanaged local bootstrap keys can diverge from official release trust anchors if not governed by deployment policy.
9. Runtime Security Gate (`runner.Run`)#
9.1 Telemetry Export Hook#
If MUTANT_SECURITY_TELEMETRY_FILE is set, runner defers telemetry JSON export
at function exit.
9.2 Standalone Trailer Validation#
Before signature verification and decode, runner performs standalone trailer validation when the binary appears to carry an appended payload.
Validation order:
- V3 trailer
- V2 trailer
- V1 trailer
Checks performed:
- trailer marker and version
- payload length bounds
- payload SHA-256
- canary (V2+)
- profile code validity and provenance hash (V3)
Failure handling:
- checksum mismatch -> reject payload
- canary mismatch -> reject payload
- provenance mismatch -> reject payload
- unsupported trailer version -> reject payload
9.3 Process-Protection Probe Enforcement#
At both pre-decode and pre-execution stages, runner evaluates process
protection probes (process_injection, trampoline, iat_got,
module_integrity, memory_page_anomaly) when
MUTANT_ENABLE_ANTITAMPER_PROBE=1.
Rollout gate:
MUTANT_ENABLE_PROCESS_PROTECTIONcontrols whether runner executes process-protection enforcement.- default is enabled when unset.
- disable values:
0,false,off,no.
Enforcement threshold:
- Any signal with
detected=trueandconfidence >= 80is treated as a process protection event.
Response path:
- Telemetry event:
process_protection_detected - Policy dispatch:
ApplyTamperResponse(process_protection_detected, ...) - Default result: terminate in secure mode, warn in compatibility/dev mode.
9.4 Remote Process Scan Enforcement#
Runner executes RunRemoteProcessScan at both pre-decode and pre-execution
stages.
Gates and modes:
MUTANT_ENABLE_REMOTE_PROCESS_SCAN=1enables scan execution.MUTANT_REMOTE_SCAN_MODE=off|observe|enforcecontrols decision behavior.- Scanner errors are telemetry-visible and non-blocking.
Current enforcement behavior:
observe: records telemetry only, never blocks execution.enforce: blocks only when a verdict reaches the configured critical score.- High-risk but non-critical verdicts remain advisory.
Current implementation status:
- Configuration, correlator, telemetry, and runner policy integration are implemented.
ScanRemoteProcessesWindowscurrently returns no verdicts (safe no-op), so remote verdict generation is scaffolding-ready but not yet signal-rich.
10. VM Runtime Protection Internals#
10.1 Offset-Aware Instruction Decode#
VM fetch path decrypts opcode/operands at exact stream offsets:
- opcode:
SecureXOROneAt(ins[ip], seed=inslen, password, offset=ip) uint16operands:ReadUint16(..., offset=ip+1)uint8operands:ReadUint8(..., offset=ip+1)
Security effect:
- avoids fixed-position-independent keystream reuse.
- ties decrypt stream byte to logical position.
10.2 Runtime Integrity Baselines#
- Each compiled function instruction slice gets SHA-256 baseline at registration.
- Main function baseline seeded at VM init.
- Additional function baselines registered when frames are pushed.
10.3 Probe Scheduler#
runIntegrityProbes() triggers checks by schedule:
- periodic: every
integrityEverysteps (default 64) - jittered: when
stepCount % 97 == integrityJitter % 97 - sweep: every 251 steps checks all active frames
10.4 Integrity Response#
If current instruction hash differs from expected baseline:
RecordIntegrityFailure(stage)ApplyTamperResponse(integrity_failed, stage, secureMode=true, err)
Important implementation detail:
- VM currently calls tamper response with
secureMode=truefor integrity failures, making secure defaults (terminate) apply unless env override downgrades policy.
10.5 Builtin Capability Gating#
Risky builtins are gated by explicit capability names and default policy from the selected protection profile.
Current capability groups:
command_execfilesystemnetwork
Behavior:
minimalprofile defaults to allow-all.standardandparanoidprofiles default-deny the risky groups.- Explicit
MUTANT_BUILTIN_CAPABILITIESoverrides the profile default.
11. In-Memory Object Protection#
11.1 VM Stack/Object Paths#
push()attempts to encrypt objects before storing.pop()attempts to decrypt encrypted objects before use.- arrays/hashes decrypt elements during construction.
- builtin call arguments decrypted before invocation.
11.2 Secure Wrappers (`object/secure_memory.go`)#
Available primitives:
SecureGlobal
- encrypted value storage, typed set/get, secure clear.
SecureStack
- optional auto-encrypt idle entries after timeout.
SecureConstantPool
- encrypted constants with cache.
Current wiring note:
- VM runtime protection path primarily uses
mutil.EncryptObjectandmutil.DecryptObjectin stack/global/constant handling. object/secure_memory.goprovides additional wrappers and utilities that can be used by integrations, but they are not the primary VM storage path today.
Current policy gate:
MUTANT_VM_GLOBAL_MEMORY_MODE=runtime|wrapper- default is
runtimefor performance-safe operation wrapperenables optional global wrapper usage where object types are supported
Current helper-scope decision:
- no additional secure-memory helper types are required for this release beyond
SecureGlobal,SecureStack, andSecureConstantPool - revisit helper expansion only if threat-model requirements exceed current runtime encryption + optional wrapper mode coverage
11.3 Stream Offset Convention for Object Types#
objectStreamOffset:
- integer -> 64
- string -> 128
- boolean -> 192
- default -> 256
Purpose:
- deterministic per-type stream segmentation and reduced overlap.
Limitations:
- plaintext exists transiently during runtime operations and conversions.
- Go string immutability limits guaranteed in-place wipe.
12. Anti-Debug Design#
12.1 Entry Point#
IsDebuggerPresent() dispatches by OS:
- windows ->
isDebuggerPresentWindows - linux ->
isDebuggerPresentLinux - darwin ->
isDebuggerPresentDarwin
12.2 Windows Signals#
High-confidence (single hit triggers):
IsDebuggerPresentCheckRemoteDebuggerPresentNtQueryInformationProcess(ProcessDebugPort)NtQueryInformationProcess(ProcessDebugObjectHandle)
Low-confidence weak hits (threshold-based):
OutputDebugStringtiming anomaly- common debugger DLL presence
Decision rule:
shouldTriggerDebuggerByWeight(highConfidence, weakHits, weakThreshold=2)
12.3 Linux Signals#
/proc/self/statusTracerPid != 0- parent commandline pattern matching for debugger/re tools
- debugger-related env markers (
GDB_*,LLDB_*,VALGRIND_*,LD_PRELOAD, etc.)
12.4 Darwin Signals#
sysctlP_TRACED flag- parent process pattern match
- debugging env markers (
LLDB_*,DYLD_INSERT_LIBRARIES, etc.)
12.5 Enforcement Staging#
Runner checks anti-debug at:
pre-decodepre-execution
On detection:
- telemetry increment
- policy application (
warn/delay/terminate)
13. Telemetry and Audit#
13.1 Counters#
Atomic counters:
debugger_detectedintegrity_failedsignature_failedsandbox_detectedprocess_protection_detectedanti_tamper_probe_invokedanti_tamper_probe_errorremote_process_scan_invokedremote_process_scan_errorremote_process_suspiciousremote_process_criticalcommand_attemptcommand_blockedcommand_succeededcommand_failed
13.2 APIs#
SecurityTelemetrySnapshot()-> mapSecurityTelemetryJSON()-> serialized JSONExportSecurityTelemetry(path)-> file write mode0600ResetSecurityTelemetry()-> test/ops reset
13.3 Audit Stream#
If MUTANT_SECURITY_AUDIT=1, writes stderr event lines:
[security-audit] ts=<unix> event=<...> stage=<...>
14. Environment Variable Contract#
14.1 Trust and Keys#
MUTANT_TRUSTED_PUBLIC_KEY_HEX
- required in secure runtime mode
- trusted signer pinning key (ed25519 public key, hex)
MUTANT_SIGNING_PRIVATE_KEY_HEX
- optional generator signing private key
- stable artifact signer identity when set
MUTANT_KEYSTORE_DIR
- optional override for local key bootstrap directory
- defaults to
<home>/.mutant/keys
14.2 Tamper Policy#
MUTANT_TAMPER_RESPONSE=warn|delay|terminateMUTANT_TAMPER_DELAY_MS= integer ms in[0..5000]
14.3 Probe and Process-Scan Gates#
MUTANT_ENABLE_ANTITAMPER_PROBE=1enables anti-tamper probe execution.MUTANT_ENABLE_PROCESS_PROTECTIONgates runner enforcement of the focused 5-probe process-protection set.MUTANT_ENABLE_REMOTE_PROCESS_SCAN=1enables remote process scan manager.MUTANT_REMOTE_SCAN_MODE=off|observe|enforce.MUTANT_REMOTE_SCAN_MAX_PROCESSES= positive integer, default32.MUTANT_REMOTE_SCAN_INTERVAL_MS= positive integer, default1000.MUTANT_REMOTE_SCAN_ALLOWLIST= comma-separated process names.
14.4 Observability#
MUTANT_SECURITY_AUDIT=1to enable stderr audit linesMUTANT_SECURITY_TELEMETRY_FILE= output path for telemetry JSON
15. Error and Failure Semantics#
15.1 Canonical Security Errors#
ErrWrongSignatureErrPasswordRequiredErrInvalidMetadataErrDebuggerDetectedErrSandboxDetectedErrProcessProtectionDetectedErrUntrustedSigner
15.2 Policy-Dependent Behavior#
Same event can either:
- terminate with original error (
terminate) - continue with warning (
warn) - continue after delay (
delay)
15.3 Design Tradeoff#
- Secure defaults maximize resistance but may impact debuggability.
- Compatibility/dev improves operator ergonomics but weakens security guarantees.
16. Detailed Dataflow#
17. Testing and Verification Strategy#
17.1 Unit/Integration Coverage Highlights#
security/security_test.go
- trusted key pinning success/failure
- argon2 bounds validation
- stream XOR offset correctness
- telemetry counters/json/export
- policy defaults and response behavior
- compatibility mixed artifact behavior
- anti-debug weighting logic
- anti-tamper probe routing and process-protection signal set
runner/runner_test.go
- secure mode malformed/tampered payload rejection
- compatibility mode continue-on-signature-failure behavior
- secure-mode termination on high-confidence process-protection signal
- advisory behavior for sub-threshold process-protection signal
vm/vm_security_policy_test.go
- integrity tamper behavior under
warn,delay,terminate
17.2 CI Security Profile#
Workflow .github/workflows/security-profile.yml:
- strict env defaults (
terminate, delay=0, audit=1) - targeted security packages + VM policy test
- optional telemetry artifact upload
18. Security Invariants (Must Hold)#
- When
--signer-authis enabled in secure mode, trusted signer verification must be enforced (trusted env key or local bootstrap trusted key). - In signer-auth path, signature mismatch/untrusted signer must fail unless policy is explicitly downgraded by env.
- Integrity mismatch must always record telemetry before policy action.
- Metadata parser must reject malformed Argon2 parameters.
- Opcode/operand decode must stay offset-aware.
- Telemetry export path must not fail open to panic.
19. Residual Risks and Limitations#
- Compatibility/dev mode can be misused in production if not controlled.
- Env-driven policy downgrades (
warn/delay) can reduce enforcement. - Userland anti-debug remains bypassable by binary patching.
- No hardware trust anchor or attestation.
- Deterministic fallback password is convenience-only and not secret.
- Full Windows runtime integration tests on real CI runners remain pending.
- Full compile-sign-run-tamper rerun lifecycle tests remain incomplete.
20. Operational Guidance#
20.1 Recommended Production Baseline#
- Launch with
--secureonly. - Set
MUTANT_TRUSTED_PUBLIC_KEY_HEXto release signer key. - Set
MUTANT_TAMPER_RESPONSE=terminate. - Set
MUTANT_TAMPER_DELAY_MS=0unless delay strategy is intentional. - Enable telemetry export and audit in monitored environments.
20.2 Developer Baseline#
- Use
--devfor local test convenience only. - Avoid using
--devartifacts/behavior as production acceptance signal. - Keep test environment policy explicit to avoid accidental drift.
21. Future Hardening Backlog (LLD-Level)#
- Add compile-sign-run-tamper-rerun end-to-end suites.
- Add real Windows runner-based anti-debug integration tests.
- Add signer key rotation and multi-key trust set support.
- Add self-integrity hashing of selected runtime text/function regions.
- Add policy lock mode to ignore environment downgrades in production builds.
- Consider hardware-backed key protection for signing workflows.
- Expand telemetry schema with per-event reason codes and monotonic sequence IDs.
22. Appendix A: Key Functions by Responsibility#
Build-Time#
generator.Generategenerator.encryptCodesecurity.AESEncryptsecurity.SignCode
Run-Time Gate#
runner.Runrunner.enforceAntiDebugrunner.decryptCode
Runtime VM#
vm.Runvm.runIntegrityProbesvm.verifyFrameIntegrity
Security Core#
security.VerifyCodesecurity.VerifyCodeWithTrustedPublicKeysecurity.ApplyTamperResponsesecurity.Record*telemetry APIssecurity.IsDebuggerPresent
23. Appendix B: Mode/Policy Decision Table#
| Execution Posture | Signature Verification Path | Default Policy | Wrong Signature / Untrusted Key | Debugger Hit | Integrity Mismatch |
|---|---|---|---|---|---|
Secure (--secure) |
Trusted key pinning required | terminate | stop by default (unless env downgrade) | stop by default (unless env downgrade) | stop by default (unless env downgrade) |
Compatibility (--compat) |
Embedded key verification | warn | continue/log by default | continue/log by default | continue/log by default |
Developer (--dev) |
Compatibility path forced | warn | continue/log by default | continue/log by default | continue/log by default |
24. Appendix C: Design Review Checklist#
- Any new security check records telemetry before response.
- Any new response path uses
ApplyTamperResponseconsistently. - Any new opcode/operand decode remains offset-aware.
- Any metadata schema changes preserve strict parse/validation behavior.
- Any new compatibility shortcut is explicitly blocked in secure mode.
- Any CI changes preserve targeted security profile execution.
End of document.