mockforge-plugin-loader 0.2.0

Plugin loader with security sandboxing and validation for MockForge
Documentation
# Plugin Signature Verification

This document explains how to use the plugin signature verification feature in MockForge.

## Overview

MockForge plugins support cryptographic signature verification using:
- **Ed25519**: Fast, modern elliptic curve signatures (recommended)
- **RSA PKCS#1 v1.5**: Traditional RSA signatures with SHA-256 (2048, 3072, or 4096-bit keys)

Signature verification ensures that plugins haven't been tampered with and come from trusted sources.

## Generating Keys

### Ed25519 Keys (Recommended)

```rust
use mockforge_plugin_loader::{generate_ed25519_keypair, save_keypair};

// Generate a new key pair
let (private_key, public_key) = generate_ed25519_keypair().unwrap();

// Save to files
save_keypair(
    &private_key,
    &public_key,
    Path::new("./keys"),
    "my-signing-key"
).unwrap();

// This creates:
// - ./keys/my-signing-key.private.key (hex-encoded private key)
// - ./keys/my-signing-key.public.key (hex-encoded public key)
```

## Signing a Plugin

To sign a plugin, you need:
1. The plugin directory containing `plugin.toml`
2. Your private key (PKCS#8 format)
3. A key ID to identify which key was used

```rust
use mockforge_plugin_loader::{sign_plugin_ed25519, load_key_from_file};
use std::path::Path;

// Load your private key
let private_key = load_key_from_file(Path::new("./keys/my-signing-key.private.key")).unwrap();

// Sign the plugin
let signature = sign_plugin_ed25519(
    Path::new("./my-plugin"),
    "my-signing-key",  // Key ID
    &private_key
).unwrap();

// This creates ./my-plugin/plugin.sig
```

## Signature File Format

The `plugin.sig` file is JSON:

```json
{
  "algorithm": "ED25519",
  "key_id": "my-signing-key",
  "signature": "hex-encoded-signature-bytes",
  "signed_content_hash": "hex-encoded-sha256-hash-of-manifest"
}
```

## Verifying Signatures

### Configuration

Configure the plugin loader with trusted keys:

```rust
use mockforge_plugin_loader::{PluginLoaderConfig, load_key_from_file};
use std::collections::HashMap;

// Load public keys
let public_key = load_key_from_file(
    Path::new("./keys/my-signing-key.public.key")
).unwrap();

// Create key data map
let mut key_data = HashMap::new();
key_data.insert("my-signing-key".to_string(), public_key);

// Configure loader
let config = PluginLoaderConfig {
    trusted_keys: vec!["my-signing-key".to_string()],
    key_data,
    allow_unsigned: false,  // Reject unsigned plugins
    ..Default::default()
};
```

### Using the Installer

The `PluginInstaller` automatically verifies signatures when `verify_signature` is enabled:

```rust
use mockforge_plugin_loader::{PluginInstaller, InstallOptions};

let installer = PluginInstaller::new(config).unwrap();

let options = InstallOptions {
    verify_signature: true,  // Enable signature verification
    force: false,
    skip_validation: false,
    expected_checksum: None,
};

// This will verify the signature before installing
installer.install("./my-plugin", options).await.unwrap();
```

### Manual Verification

You can also verify signatures manually:

```rust
use mockforge_plugin_loader::SignatureVerifier;

let verifier = SignatureVerifier::new(&config);
let result = verifier.verify_plugin_signature(Path::new("./my-plugin"));

match result {
    Ok(_) => println!("✓ Signature verified successfully"),
    Err(e) => eprintln!("✗ Signature verification failed: {}", e),
}
```

## Security Considerations

### Verification Process

The signature verification process:
1. Reads `plugin.sig` from the plugin directory
2. Checks that the signing key ID is in the trusted keys list
3. Retrieves the corresponding public key from `key_data`
4. Computes SHA-256 hash of `plugin.toml`
5. Verifies the hash matches the signed content hash
6. Verifies the cryptographic signature using the public key

### What is Protected

- **Plugin Manifest**: The `plugin.toml` file is signed and verified
- **Tampering Detection**: Any modification to the manifest after signing will cause verification to fail
- **Key Trust**: Only signatures from trusted keys are accepted

### What is NOT Protected

- **Plugin Code**: The WASM binary itself is not directly signed (only the manifest that references it)
- **Configuration Files**: Additional files in the plugin directory are not signed
- **Replay Attacks**: Signatures don't include timestamps or nonces

### Best Practices

1. **Keep Private Keys Secure**: Never commit private keys to version control
2. **Use Strong Keys**: Ed25519 (recommended) or RSA 3072/4096-bit
3. **Rotate Keys**: Periodically generate new keys and re-sign plugins
4. **Maintain Key Lists**: Keep trusted keys list minimal and up-to-date
5. **Disable Unsigned in Production**: Set `allow_unsigned: false` in production
6. **Verify Sources**: Only add keys from trusted plugin developers

## Development Mode

For development, you can allow unsigned plugins:

```rust
let config = PluginLoaderConfig {
    allow_unsigned: true,  // Allow unsigned plugins
    ..Default::default()
};
```

This is useful during plugin development but should be disabled in production.

## Troubleshooting

### "No signature file found"
- The plugin directory must contain `plugin.sig`
- If `allow_unsigned: false`, all plugins must be signed

### "Signature key is not in trusted keys list"
- Add the key ID to `config.trusted_keys`
- Ensure the key ID in `plugin.sig` matches a trusted key

### "Public key data not found"
- Add the public key bytes to `config.key_data`
- Ensure the key ID matches between `trusted_keys` and `key_data`

### "Signature verification failed"
- The signature is invalid or corrupted
- The public key doesn't match the private key used for signing
- The plugin manifest was modified after signing

### "Plugin manifest hash does not match"
- The `plugin.toml` was modified after signing
- Re-sign the plugin with the correct manifest content

## Example: Complete Workflow

```rust
use mockforge_plugin_loader::*;
use std::collections::HashMap;
use std::path::Path;

// 1. Generate keys (one-time setup)
let (private_key, public_key) = generate_ed25519_keypair().unwrap();
save_keypair(&private_key, &public_key, Path::new("."), "dev-key").unwrap();

// 2. Sign your plugin
sign_plugin_ed25519(
    Path::new("./my-plugin"),
    "dev-key",
    &private_key
).unwrap();

// 3. Configure loader with public key
let mut key_data = HashMap::new();
key_data.insert("dev-key".to_string(), public_key);

let config = PluginLoaderConfig {
    trusted_keys: vec!["dev-key".to_string()],
    key_data,
    allow_unsigned: false,
    ..Default::default()
};

// 4. Install plugin with verification
let installer = PluginInstaller::new(config).unwrap();
let options = InstallOptions {
    verify_signature: true,
    ..Default::default()
};

installer.install("./my-plugin", options).await.unwrap();
println!("Plugin installed and verified!");
```

## API Reference

### Key Generation
- `generate_ed25519_keypair() -> (Vec<u8>, Vec<u8>)` - Generate Ed25519 key pair

### Key Management
- `save_keypair(private, public, dir, name)` - Save key pair to files
- `load_key_from_file(path) -> Vec<u8>` - Load hex-encoded key from file

### Signing
- `sign_plugin_ed25519(plugin_dir, key_id, private_key) -> PluginSignature` - Sign a plugin

### Verification
- `SignatureVerifier::new(config) -> SignatureVerifier` - Create verifier
- `verifier.verify_plugin_signature(plugin_dir) -> Result<()>` - Verify plugin signature

### Types
- `SignatureAlgorithm` - Enum of supported algorithms (Ed25519, RSA variants)
- `PluginSignature` - Signature metadata structure
- `PluginLoaderConfig` - Configuration with `trusted_keys` and `key_data`