mirror of
https://github.com/luxfi/mpc.git
synced 2026-09-24 00:26:24 +00:00
Fixes all findings from the Blue Team defensive security review of the redacted → redacted custody architecture transition. HIGH: - S-1: Implement P-256 ECDH + AES-256-GCM encryption of passkey backup shards - W-2: Reject unparseable WebAuthn public keys (was silently skipping verification) - O-1: Add Ed25519 binary signature verification package (pkg/integrity) MEDIUM: - W-1: Validate attestation is a real P-256 curve point, not raw attestation object - W-3: Add origin validation to all WebAuthn flows (register, verify, trade approve) - N-2: Remove plaintext TCP fallback from MPC node transport (TLS mandatory) - K-1: Fail-closed on empty/whitespace orgID (prevents cross-tenant key leakage) - A-1: Make AuditEntry immutable (Update/Delete return error) - A-2: Add MPC-level signing audit log (wallet, message hash, intent, trigger) - O-3: Invalidate backup shards after key reshare (stale shares are useless) - O-2: Reject env/file password providers in production (require cloud HSM) LOW: - T-1: Add error handling + MarkSignFailed to signing goroutine - T-2: Verify caller authorization in handleTradeSubmit - K-2: Secret erasure stub now calls runtime.GC() instead of no-op - N-1: Add VerifyConnection callback to reject peers without TLS certs Tests: 69 unit + 7 fuzz targets, ~900K fuzz executions, 19/19 packages green. Makefile: add -tags json1 for SQLite JSON function support in ORM tests.
92 lines
2.8 KiB
Go
92 lines
2.8 KiB
Go
// Package integrity provides Ed25519 signature verification for MPC binaries
|
|
// and configuration files. This prevents code injection via compromised S3
|
|
// buckets or supply-chain attacks on binary distribution.
|
|
package integrity
|
|
|
|
import (
|
|
"crypto/ed25519"
|
|
"crypto/sha256"
|
|
"encoding/hex"
|
|
"fmt"
|
|
"os"
|
|
)
|
|
|
|
// TrustedSigningKeys contains the Ed25519 public keys authorized to sign MPC
|
|
// binaries. The binary signature file (<binary>.sig) must be verifiable by at
|
|
// least one of these keys. Keys are hex-encoded 32-byte Ed25519 public keys.
|
|
//
|
|
// These are baked into the binary at compile time. To rotate, update this list
|
|
// and rebuild. The old key should be kept for one release cycle to allow
|
|
// rollback.
|
|
var TrustedSigningKeys []ed25519.PublicKey
|
|
|
|
// SetTrustedKeys sets the signing keys. Called from main() with keys loaded
|
|
// from config or hardcoded.
|
|
func SetTrustedKeys(hexKeys []string) error {
|
|
TrustedSigningKeys = nil
|
|
for _, h := range hexKeys {
|
|
b, err := hex.DecodeString(h)
|
|
if err != nil || len(b) != ed25519.PublicKeySize {
|
|
return fmt.Errorf("invalid signing key: %s", h)
|
|
}
|
|
TrustedSigningKeys = append(TrustedSigningKeys, ed25519.PublicKey(b))
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// VerifyFile checks that a file's Ed25519 signature is valid against at least
|
|
// one trusted signing key. The signature file must exist at <path>.sig and
|
|
// contain a hex-encoded 64-byte Ed25519 signature of SHA-256(file contents).
|
|
func VerifyFile(path string) error {
|
|
if len(TrustedSigningKeys) == 0 {
|
|
return fmt.Errorf("no trusted signing keys configured")
|
|
}
|
|
|
|
data, err := os.ReadFile(path)
|
|
if err != nil {
|
|
return fmt.Errorf("read file: %w", err)
|
|
}
|
|
|
|
sigHex, err := os.ReadFile(path + ".sig")
|
|
if err != nil {
|
|
return fmt.Errorf("read signature file %s.sig: %w", path, err)
|
|
}
|
|
|
|
sig, err := hex.DecodeString(string(sigHex))
|
|
if err != nil || len(sig) != ed25519.SignatureSize {
|
|
return fmt.Errorf("invalid signature format in %s.sig", path)
|
|
}
|
|
|
|
hash := sha256.Sum256(data)
|
|
|
|
for _, key := range TrustedSigningKeys {
|
|
if ed25519.Verify(key, hash[:], sig) {
|
|
return nil
|
|
}
|
|
}
|
|
|
|
return fmt.Errorf("signature verification failed: no trusted key signed %s", path)
|
|
}
|
|
|
|
// HashFile returns the hex-encoded SHA-256 hash of a file.
|
|
func HashFile(path string) (string, error) {
|
|
data, err := os.ReadFile(path)
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
hash := sha256.Sum256(data)
|
|
return hex.EncodeToString(hash[:]), nil
|
|
}
|
|
|
|
// SignFile creates a signature file for the given path using the provided
|
|
// Ed25519 private key. The signature covers SHA-256(file contents).
|
|
func SignFile(path string, privKey ed25519.PrivateKey) error {
|
|
data, err := os.ReadFile(path)
|
|
if err != nil {
|
|
return fmt.Errorf("read file: %w", err)
|
|
}
|
|
hash := sha256.Sum256(data)
|
|
sig := ed25519.Sign(privKey, hash[:])
|
|
return os.WriteFile(path+".sig", []byte(hex.EncodeToString(sig)), 0644)
|
|
}
|