The signature verification system provides production-ready cryptographic verification for LoRA adapters and model weights using OpenSSL. It implements RSA-SHA256 signature verification with X.509 certificate validation.
The system uses the Chain of Responsibility and Builder patterns:
┌─────────────────────────────────────────────────────────┐
│ SignatureVerifierBuilder │
│ (Fluent interface for building verification chains) │
└─────────────────────┬───────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────┐
│ ISignatureVerifier (Base Class) │
└─────┬───────────────────┬──────────────────┬────────────┘
│ │ │
▼ ▼ ▼
┌──────────────┐ ┌──────────────────┐ ┌──────────┐
│ RSA_SHA256 │ │ CertificateChain │ │ CRL │
│ Verifier │→ │ Verifier │→ │ Checker │
└──────────────┘ └──────────────────┘ └──────────┘
#include "llm/security/signature_verifier.h"
using namespace themis::llm::security;
// Create verifier
RSA_SHA256_Verifier verifier;
// Load data and signature
std::vector<uint8_t> data = /* your data */;
std::vector<uint8_t> signature = /* signature bytes */;
std::string cert_pem = /* X.509 certificate in PEM format */;
// Verify
auto result = verifier.verify(data, signature, cert_pem);
if (result.is_valid) {
std::cout << "Signature valid!" << std::endl;
std::cout << "Signer: " << result.signer_identity << std::endl;
} else {
std::cerr << "Signature invalid: " << result.error_message << std::endl;
}#include "llm/security/signature_verifier.h"
using namespace themis::llm::security;
// Build verification chain with RSA, certificate chain, and CRL checks
SignatureVerifierBuilder builder;
auto verifier = builder
.withRSA_SHA256()
.withCertificateChainValidation("/etc/ssl/certs/ca-certificates.crt")
.withCRLCheck("http://crl.example.com/adapter.crl")
.build();
// Verify with full chain
auto result = verifier->verify(data, signature, cert_pem);
if (result.is_valid && result.chain_valid) {
std::cout << "Full verification passed!" << std::endl;
} else {
std::cerr << "Verification failed: " << result.error_message << std::endl;
}Performs cryptographic RSA-SHA256 signature verification.
Features:
- Loads X.509 certificates from PEM format
- Extracts and validates RSA public keys
- Enforces minimum 2048-bit key size
- Computes SHA-256 hash of data
- Verifies signature using OpenSSL EVP API
Usage:
RSA_SHA256_Verifier verifier;
auto result = verifier.verify(data, signature, cert_pem);Validates X.509 certificate chains against trusted CAs.
Features:
- Loads CA bundles from system or custom paths
- Validates certificate chain to trusted root
- Checks certificate expiration
- Detects self-signed certificates
- Reports specific validation errors
Usage:
CertificateChainVerifier verifier("/etc/ssl/certs/ca-certificates.crt");
auto result = verifier.verify(data, signature, cert_pem);
if (!result.chain_valid) {
std::cerr << "Chain validation failed" << std::endl;
}Supported CA Bundle Paths (auto-detected):
/etc/ssl/certs/ca-certificates.crt(Debian/Ubuntu)/etc/pki/tls/certs/ca-bundle.crt(RHEL/CentOS)/etc/ssl/ca-bundle.pem(OpenSUSE)/usr/local/share/certs/ca-root-nss.crt(FreeBSD)- System default paths
Checks if certificates are revoked via Certificate Revocation Lists.
Features:
- CRL checking framework
- Graceful handling of unavailable CRLs
- Certificate serial number extraction
- Falls back on error (doesn't block validation)
Usage:
CRLChecker checker("http://crl.example.com/adapter.crl");
auto result = checker.verify(data, signature, cert_pem);Note: Full CRL download requires HTTP client integration (planned).
Fluent interface for building verification chains.
Usage:
SignatureVerifierBuilder builder;
// Add verifiers in desired order
auto verifier = builder
.withRSA_SHA256() // Step 1: RSA verification
.withCertificateChainValidation("/path/to/ca") // Step 2: Chain validation
.withCRLCheck("http://crl.url") // Step 3: Revocation check
.build();
// Execute verification chain
auto result = verifier->verify(data, signature, cert_pem);struct SignatureVerificationResult {
bool is_valid; // Overall validity
std::string algorithm; // Algorithm used (e.g., "RSA-SHA256")
std::string signer_identity; // Subject from certificate
std::string error_message; // Error details if invalid
std::vector<std::string> chain_fingerprints; // Chain cert fingerprints
bool chain_valid; // Certificate chain validity
};The signature verifier is integrated with LoRASecurityValidator:
#include "llm/lora_security_validator.h"
using namespace themis::llm;
LoRASecurityConfig config;
config.require_signature = true;
config.trusted_signers = {"cert_fingerprint_1", "cert_fingerprint_2"};
LoRASecurityValidator validator(config);
// Verify LoRA adapter signature
auto result = validator.verifyEmbeddedSignature("path/to/adapter.safetensors");
if (result.is_valid) {
std::cout << "LoRA adapter signature verified" << std::endl;
} else {
std::cerr << "Invalid signature: " << result.error_message << std::endl;
}- ✅ Minimum 2048-bit RSA keys (enforced)
- ✅ SHA-256 hash algorithm (not SHA-1)
- ✅ X.509 v3 certificates
- ✅ Certificate chain to trusted CA
- ✅ Detailed error messages
| Threat | Mitigation |
|---|---|
| Data tampering | Signature verification fails on modified data |
| Malicious adapter | Unsigned or improperly signed adapter rejected |
| Compromised key | CRL check fails (when CRL available) |
| Man-in-the-middle | Certificate chain validation fails |
| Weak keys | 1024-bit and smaller keys rejected |
- Constant-time comparison (timing attack resistance)
- Certificate pinning (optional)
- Rate limiting for verification (DoS prevention)
- Full CRL download/caching
cd tests/data/certificates
./generate_test_certs.shThis creates:
- CA certificate and key
- Test certificates (2048, 3072, 4096-bit)
- Self-signed certificate
- Expired certificate
- Weak 1024-bit certificate
- Test data and signatures
Minimal standalone test (no dependencies):
cd tests
./test_signature_minimal.shGTest suite (requires full build):
cd build
ctest -R SignatureVerifierTests# Generate 2048-bit RSA key
openssl genrsa -out lora_key.pem 2048
# Create certificate signing request
openssl req -new -key lora_key.pem -out lora.csr \
-subj "/C=US/ST=CA/O=YourOrg/CN=lora-signer"
# Sign with your CA (or create self-signed for testing)
openssl x509 -req -in lora.csr -CA ca_cert.pem -CAkey ca_key.pem \
-CAcreateserial -out lora_cert.pem -days 365 -sha256# Sign the adapter file
openssl dgst -sha256 -sign lora_key.pem \
-out adapter_signature.bin adapter.safetensors// Load adapter, signature, and certificate
auto adapter_data = loadFile("adapter.safetensors");
auto signature = loadFile("adapter_signature.bin");
auto cert_pem = loadFile("lora_cert.pem");
// Verify
RSA_SHA256_Verifier verifier;
auto result = verifier.verify(adapter_data, signature, cert_pem);
if (result.is_valid) {
// Load and use the adapter
loadLoRAAdapter("adapter.safetensors");
}Typical verification times (on modern CPU):
- RSA-2048 verification: < 1ms
- RSA-4096 verification: < 5ms
- Certificate chain validation: < 10ms
- Full chain (RSA + chain + CRL): < 15ms
Common error messages and meanings:
| Error | Meaning |
|---|---|
| "Data is empty" | Input data vector is empty |
| "Signature is empty" | Signature vector is empty |
| "Certificate is empty" | Certificate PEM string is empty |
| "Failed to load certificate" | Invalid PEM format |
| "Failed to extract public key" | Certificate doesn't contain valid public key |
| "RSA key size too small: X bits" | Key is smaller than 2048 bits |
| "Signature verification failed: signature does not match" | Signature is invalid (data tampered or wrong key) |
| "Certificate chain validation failed" | Certificate not trusted by CA bundle |
| "Certificate has expired" | Certificate validity period has ended |
| "Self-signed certificate" | Certificate not signed by trusted CA |
- Always validate certificate chains in production
- Use strong keys (≥2048 bits, prefer 3072 or 4096)
- Keep CA bundles updated for security
- Log verification failures for security auditing
- Use CRL checking when available
- Rotate keys regularly (annually recommended)
- Store private keys securely (HSM for production)
- Never commit private keys to source control
- OpenSSL EVP API: https://www.openssl.org/docs/man3.0/man3/EVP_PKEY_verify.html
- X.509 Certificates: https://www.openssl.org/docs/man3.0/man3/X509_verify_cert.html
- RSA Best Practices: https://nvlpubs.nist.gov/nistpubs/SpecialPublications/NIST.SP.800-57pt1r5.pdf
For issues or questions:
- Check error messages for specific failure reasons
- Review test certificates in
tests/data/certificates/ - Run standalone tests to verify OpenSSL installation
- Check system CA bundle paths are accessible
Version: 1.0
Status: Production Ready
Last Updated: 2026-04-06