Signing & Verification¶
Secure your FlavorPack packages with Ed25519 digital signatures for authenticity and integrity.
๐ค AI-Generated Content
This documentation was generated with AI assistance and is still being audited. Some, or potentially a lot, of this information may be inaccurate. Learn more.
Overview¶
FlavorPack uses Ed25519 digital signatures to ensure packages haven't been tampered with and come from trusted sources. This guide covers key generation, package signing, verification, and best practices for secure distribution.
Quick Start¶
Generate Keys¶
# Generate a new Ed25519 key pair
flavor keygen --out-dir keys
# This creates:
# - keys/flavor-private.key (private key - keep secret!)
# - keys/flavor-public.key (public key - distribute freely)
Sign Package¶
# Sign during build
flavor pack --manifest pyproject.toml --private-key keys/flavor-private.key --public-key keys/flavor-public.key
# Package is now signed and can be verified
Verify Package¶
Trusted Key Verification
The verify command checks the signature against the public key embedded in the package. To check whether a package was signed by a key in your local trusted key store, use flavor trust verify package.psp.
Key Management¶
Key Generation Options¶
1. Random Keys (Recommended for Production)¶
Generate cryptographically secure random keys:
# Generate with default settings (creates keys/ directory)
flavor keygen
# Specify custom output directory
flavor keygen --out-dir ~/.flavor/keys
2. Using Existing Keys¶
Use existing Ed25519 key files:
# Keys must be in PEM format
flavor pack --manifest pyproject.toml \
--private-key /path/to/flavor-private.key \
--public-key /path/to/flavor-public.key
Note: Keys must be Ed25519 format in PEM encoding. The private key file should be 32 bytes (raw seed) or PEM-encoded Ed25519 private key.
Key Storage Best Practices¶
Development¶
# Store in home directory
mkdir -p ~/.flavor/keys
chmod 700 ~/.flavor/keys
flavor keygen --out-dir ~/.flavor/keys
chmod 600 ~/.flavor/keys/flavor-private.key
Production¶
-
Encrypted Storage
-
Secret Management
- Store private key in secret manager (AWS Secrets Manager, HashiCorp Vault, etc.)
- Retrieve at build time via environment variables or secret injection
- Never commit private keys to version control
CI/CD¶
# GitHub Actions with secrets
- name: Sign package
env:
FLAVOR_KEY_SEED: ${{ secrets.SIGNING_SEED }}
run: |
flavor pack --manifest pyproject.toml --key-seed "$FLAVOR_KEY_SEED"
# GitLab CI with protected variables
sign:
script:
- flavor pack --manifest pyproject.toml --key-seed "$CI_SIGNING_SEED"
only:
- tags
Key Rotation¶
Implement regular key rotation by rebuilding packages with new keys:
# Generate new key
flavor keygen --out-dir keys/2024-01
# Rebuild packages with new key
for manifest in projects/*/pyproject.toml; do
flavor pack --manifest "$manifest" \
--private-key keys/2024-01/flavor-private.key \
--public-key keys/2024-01/flavor-public.key
done
# Archive old key
mv keys/2023-12 keys/archive/
Signing Process¶
How Signing Works¶
- Metadata Hash: Package metadata is serialized and hashed with SHA-256
- Digital Signature: Hash is signed with Ed25519 private key
- Embedding: Public key and signature are embedded in package index block
- Verification: Signature can be verified using embedded or external public key
Build-Time Signing¶
All signing happens during package build with flavor pack:
# Basic signing with key files
flavor pack --manifest pyproject.toml \
--private-key keys/flavor-private.key \
--public-key keys/flavor-public.key
# With deterministic seed (for reproducible builds)
flavor pack --manifest pyproject.toml --key-seed "secret-seed"
# Signing is automatic - no separate sign command needed
No Post-Build Signing
FlavorPack does not support signing packages after they've been built. Signing happens only during flavor pack. To re-sign a package, rebuild it with new keys.
Batch Building with Signing¶
Build and sign multiple packages:
#!/bin/bash
# build-and-sign-all.sh
PRIVATE_KEY="$1"
PUBLIC_KEY="$2"
for manifest in projects/*/pyproject.toml; do
echo "Building and signing $manifest..."
flavor pack --manifest "$manifest" \
--private-key "$PRIVATE_KEY" \
--public-key "$PUBLIC_KEY"
done
Verification¶
Automatic Verification¶
Packages are automatically verified when executed:
# Launcher verifies signature before extraction
./myapp.psp
# Disable verification (DANGEROUS - development only!)
FLAVOR_VALIDATION=none ./myapp.psp
Manual Verification¶
Basic Verification¶
# Verify with embedded public key
flavor verify package.psp
# Output:
# โ
Signature valid
# Package: myapp v1.0.0
# Signed by: SHA256:abc123...
Deep Verification¶
Planned Feature: Advanced verification modes are planned for a future release. Currently, the
verifycommand performs comprehensive verification of all components.
# Verify all components (standard verification)
flavor verify package.psp
# Output:
# โ
Index block valid
# โ
Metadata signature valid
# โ
All slot checksums valid
# โ
Package integrity confirmed
Verification Against a Trusted Key Store¶
Use flavor trust verify to check that a package was signed by a key you explicitly trust:
# First, import the signer's public key
flavor trust add signer.pub
# Then verify the package against the trusted key store
flavor trust verify package.psp
# Exit 0 = signed by a trusted key
# Non-zero = key not trusted or signature mismatch
# List all trusted keys and their fingerprints
flavor trust list
# Remove a key by fingerprint
flavor trust remove <64-hex-char fingerprint>
flavor trust verify matches the attestation_key_fp field in the package index against all keys stored under ~/.config/flavor/trusted-keys/ (user) or /etc/flavor/trusted-keys/ (system). The fingerprint is the hex-encoded SHA-256 of the raw Ed25519 public key bytes (64-character string).
Programmatic Verification¶
from pathlib import Path
from flavor.package import verify_package
# Verify package
result = verify_package(Path("package.psp"))
if result["signature_valid"]:
print("โ
Package signature verified")
else:
print("โ Invalid signature!")
Trust Models¶
1. Self-Signed (Default)¶
Package contains its own public key:
Use Cases: - Internal distribution - Development packages - Personal projects
Verification:
2. Trusted Key Store¶
Distribute signing public keys to recipients who register them locally. FlavorPack maintains a per-user and system-wide trusted key store:
- User store:
~/.config/flavor/trusted-keys/(keys added withflavor trust add) - System store:
/etc/flavor/trusted-keys/(managed by administrators)
Each key is stored as a PEM file. The fingerprint is sha256(raw_ed25519_public_key_bytes).hexdigest() โ a 64-character hex string.
Distribution workflow:
# Signer generates a key pair
flavor keygen --out-dir keys/
# Signer distributes public key to recipients via secure channel
scp keys/flavor-public.key user@server:~
# Or via configuration management
ansible-playbook deploy-keys.yml
Recipient registers and verifies:
# Import the signer's public key
flavor trust add flavor-public.key
# List trusted keys (shows fingerprint and path)
flavor trust list
# Verify any package signed by that key
flavor trust verify package.psp
flavor trust verify reads the attestation_key_fp field in the package index and checks it against all keys in the trusted stores. This confirms not just integrity but that the package came from a specific known signer.
3. Web of Trust (Future)¶
Planned Feature
Multiple signatures from trusted parties (N-of-M threshold schemes) are planned for a future release. Currently FlavorPack supports a single signer per package with trusted key store verification.
4. Certificate Authority (Future)¶
X.509 certificate chains:
Key Distribution¶
Public Key Format¶
FlavorPack generates keys in PEM format:
# Generate keys
flavor keygen --out-dir keys
# Public key is in PEM format
cat keys/flavor-public.key
# -----BEGIN PUBLIC KEY-----
# ...
# -----END PUBLIC KEY-----
Key Format Conversion
For other formats (SSH, JWK, etc.), use standard tools like ssh-keygen or openssl to convert the PEM-formatted public key.
Distribution Channels¶
1. Package Metadata¶
The public key is automatically embedded in every signed package's index block. Recipients can extract it for verification:
2. Key Servers¶
# Upload to key server
curl -X POST https://keys.example.com/upload \
-F "key=@public.pem" \
-F "email=team@example.com"
3. DNS Records¶
4. Version Control¶
# Commit public keys (never private!)
git add keys/public/*.pem
git commit -m "Add signing public keys"
Security Best Practices¶
Do's โ ¶
-
Generate keys on secure systems
-
Use unique keys per environment
-
Rotate keys regularly
-
Verify packages before distribution
-
Log signature verification
Don'ts โ¶
-
Never commit private keys
-
Never share private keys
-
Never use weak seeds
-
Never ignore verification failures
Troubleshooting¶
Common Issues¶
"Private key not found"¶
# Check file exists and permissions
ls -la private.pem
# Should show: -rw------- (600)
# Fix permissions
chmod 600 private.pem
"Invalid signature"¶
# Verify package
flavor verify package.psp
# Check package integrity with checksum
sha256sum package.psp
# If corrupted, rebuild the package with correct keys
flavor pack --manifest pyproject.toml \
--private-key keys/flavor-private.key \
--public-key keys/flavor-public.key
"Key format not recognized"¶
# Convert to PEM format
openssl pkey -in key.der -inform DER -out key.pem
# Verify key type
openssl pkey -in key.pem -text | head -1
# Should show: "ED25519 Private-Key"
Debugging¶
# Verbose verification
FOUNDATION_LOG_LEVEL=debug flavor verify package.psp
# Inspect signature details
flavor inspect package.psp
# The inspect command shows:
# - Package signature status
# - Embedded public key (first 16 bytes)
# - Format version and metadata
Advanced Topics¶
Trusted Key Store¶
The trusted key store lets operators control which signers are allowed in their environment.
How fingerprints work:
A key fingerprint is the hex-encoded SHA-256 digest of the raw (32-byte) Ed25519 public key bytes:
import hashlib
from cryptography.hazmat.primitives.serialization import Encoding, PublicFormat
raw_bytes = public_key.public_bytes(Encoding.Raw, PublicFormat.Raw)
fingerprint = hashlib.sha256(raw_bytes).hexdigest() # 64-char hex string
The PSPF index block stores the first 32 bytes of this fingerprint string in the attestation_key_fp field. flavor trust verify reads this field and cross-references it with the keys on disk.
Key store locations:
| Scope | Path |
|---|---|
| User | ~/.config/flavor/trusted-keys/ |
| System | /etc/flavor/trusted-keys/ |
Both directories are scanned; either match is sufficient.
Full workflow:
# 1. Generate key pair
flavor keygen --out-dir keys/
# 2. Sign package at build time
flavor pack --manifest pyproject.toml \
--private-key keys/flavor-private.key \
--public-key keys/flavor-public.key
# 3. Distribute public key to recipients
scp keys/flavor-public.key ops@deploy-host:~
# 4. Recipient registers the key
flavor trust add flavor-public.key
# 5. Recipient verifies any package from that signer
flavor trust verify app.psp
# 6. Remove a key when it is no longer trusted
flavor trust remove <64-hex-char fingerprint>
Multi-Signature Packages (Planned)¶
Future Feature
Threshold signature schemes (N-of-M signatures required) are planned for a future release. Currently FlavorPack supports a single signer per package with trusted key store verification.
Hardware Token Integration (Planned)¶
Future Feature
PKCS#11 hardware token support (YubiKey, HSM, etc.) is planned for a future release.
Planned workflow:
Notarization (Platform-Specific)¶
Platform-Specific
For macOS code signing and notarization, use Apple's standard tools after building:
Related Documentation¶
- Cryptographic Specification - Technical details
- Security Model - Security architecture
- Package Verification - API reference
- Troubleshooting - Common issues