Skip to content

Design: KeyForge Security

Responsibility: Cryptographic signing and secret management. Tier: 2 (The Shield) Dependencies: ed25519-dalek, sha2, zeroize, hex.

1. Public API

Function Purpose
generate_keypair() Generate Ed25519 keypair (hex strings)
generate_nonce() Generate cryptographic random u64
sign_result(secret_hex, ...) Sign result from hex key
sign_result_direct(SigningKey, ...) Sign with pre-loaded key
verify_result(public_hex, ..., sig_hex) Verify signature

2. Result Signing Protocol

To prevent malicious workers from submitting fake high scores, every result is signed using Ed25519.

sequenceDiagram
    participant Worker
    participant Sec as keyforge-security
    participant Hive

    Note over Worker: Optimization Complete

    Worker->>Sec: sign_result(secret_hex, job_id, layout, score, timestamp, nonce)
    Sec->>Sec: build_payload()
    Note right of Sec: SHA256(job_id) || SHA256(layout)<br/>|| score_fixed (i64)<br/>|| timestamp || nonce
    Sec->>Sec: Ed25519.sign(payload, SigningKey)
    Sec-->>Worker: signature_hex

    Worker->>Hive: Submit(result + signature)

    Hive->>Sec: verify_result(public_hex, ..., signature_hex)
    Sec->>Sec: Rebuild payload
    Sec->>Sec: Ed25519.verify(payload, signature, VerifyingKey)
    alt Valid
        Sec-->>Hive: Ok(true)
        Hive->>Hive: Accept Result
    else Invalid
        Sec-->>Hive: Ok(false)
        Hive->>Hive: Reject & Flag Node
    end

Payload Structure

The signed payload is deterministic (88 bytes):

Field Size Description
job_hash 32 bytes SHA256(job_id)
layout_hash 32 bytes SHA256(layout)
score_fixed 8 bytes (score * SCORE_SCALE) as i64 (LE)
timestamp 8 bytes Unix timestamp (LE)
nonce 8 bytes Random nonce (LE)

Note: Score is converted to fixed-point before signing to ensure deterministic signatures across platforms.

3. Secret Management

SecretBytes

Zeroized wrapper for sensitive binary data (e.g., private keys):

classDiagram
    class SecretBytes {
        -Vec~u8~ inner
        +new(data) SecretBytes
        +as_slice() &[u8]
    }
    note for SecretBytes "Implements Zeroize + ZeroizeOnDrop\nDebug shows '***REDACTED***'"

SecretString

Zeroized wrapper for sensitive text (e.g., passwords, API keys):

classDiagram
    class SecretString {
        -String inner
        +new(s) SecretString
        +as_str() &str
    }
    note for SecretString "Implements Zeroize + ZeroizeOnDrop\nDebug shows '***REDACTED***'"

4. Error Handling

classDiagram
    class SecurityError {
        <<enum>>
        Encoding(String)
        Key(String)
        Signature(String)
    }
Variant Cause
Encoding Invalid hex encoding
Key Invalid key length or format
Signature Invalid signature length or verification failure

5. Security Properties

Property Implementation
Memory Safety SecretBytes/SecretString use zeroize to wipe data on drop
No Logging Secrets never implement Display; Debug shows ***REDACTED***
Whitespace Tolerance Hex inputs are trimmed before parsing
Deterministic Signing Fixed-point score conversion via SCORE_SCALE
Replay Prevention Cryptographic nonce via generate_nonce()

6. Keypair Generation

pub fn generate_keypair() -> (String, String) {
    // Returns (signing_key_hex, verifying_key_hex)
    // Uses OsRng (CSPRNG)
}

Workers generate a keypair on first run and register the public key with Hive.

7. Module Structure

keyforge-security/src/
└── lib.rs   # All types and functions

Single-file crate for cryptographic primitives: - SecretBytes, SecretString - zeroized wrappers - SecurityError, SecurityResult<T> - error types - generate_keypair(), generate_nonce() - key/nonce generation - sign_result(), sign_result_direct() - signing - verify_result() - verification