PQC Signature Verification Guide for Dogenals v2.0
How to verify Falcon-512 and ML-DSA-44 signatures in Dogenals inscriptions
Overview
Dogenals v2.0 requires quantum-safe signatures for new inscriptions. This guide provides step-by-step instructions for developers implementing signature verification in indexers, wallets, and tools.
Supported Algorithms
Falcon-512 (Recommended)
- Key Size: 897 bytes (public), 1281 bytes (secret)
- Signature Size: 666-752 bytes
- Security Level: NIST Category 5 (highest)
- Performance: Fast signing/verification
ML-DSA-44 (Alternative)
- Key Size: 1312 bytes (public), 2560 bytes (secret)
- Signature Size: 2420 bytes
- Security Level: NIST Category 4
- Performance: Slower but more conservative
Hybrid Mode
- Combined: Falcon-512 + ECDSA
- Total Size: ~730-820 bytes
- Use Case: Transition period during quantum migration
Python Implementation
Dependencies
pip install pycryptodome dilithium # For ML-DSA
# Note: Falcon-512 requires custom implementation or libfalconFalcon-512 Verification
import hashlib
import falcon512 # Custom implementation needed
def verify_falcon512_signature(pubkey_hex: str, signature_hex: str, message: bytes) -> bool:
"""Verify a Falcon-512 signature.
Args:
pubkey_hex: Public key as hex string (897 bytes)
signature_hex: Signature as hex string (666-752 bytes)
message: Message bytes to verify against
Returns:
bool: True if signature is valid
"""
try:
pubkey = bytes.fromhex(pubkey_hex)
signature = bytes.fromhex(signature_hex)
# Verify key size
if len(pubkey) != 897:
return False
if not (666 <= len(signature) <= 752):
return False
# Hash message to get digest
digest = hashlib.sha256(message).digest()
# Verify signature
return falcon512.verify(pubkey, signature, digest)
except Exception:
return FalseML-DSA-44 Verification
from dilithium import Dilithium2 # ML-DSA-44 equivalent
def verify_mldsa44_signature(pubkey_hex: str, signature_hex: str, message: bytes) -> bool:
"""Verify an ML-DSA-44 signature.
Args:
pubkey_hex: Public key as hex string (1312 bytes)
signature_hex: Signature as hex string (2420 bytes)
message: Message bytes to verify against
Returns:
bool: True if signature is valid
"""
try:
pubkey = bytes.fromhex(pubkey_hex)
signature = bytes.fromhex(signature_hex)
# Verify sizes
if len(pubkey) != 1312:
return False
if len(signature) != 2420:
return False
# Create verifier instance
verifier = Dilithium2(public_key=pubkey)
# Verify signature
return verifier.verify(message, signature)
except Exception:
return FalseECDSA Verification (Legacy)
import ecdsa
import hashlib
def verify_ecdsa_signature(pubkey_hex: str, signature_hex: str, message: bytes) -> bool:
"""Verify an ECDSA signature (legacy, quantum-vulnerable).
Args:
pubkey_hex: Compressed public key as hex string (66 chars)
signature_hex: DER signature as hex string
message: Message bytes to verify against
Returns:
bool: True if signature is valid
"""
try:
# Decode public key
pubkey_bytes = bytes.fromhex(pubkey_hex)
vk = ecdsa.VerifyingKey.from_string(pubkey_bytes, curve=ecdsa.SECP256k1)
# Decode signature
signature_bytes = bytes.fromhex(signature_hex)
# Hash message
digest = hashlib.sha256(message).digest()
# Verify
return vk.verify(signature_bytes, digest, sigdecode=ecdsa.util.sigdecode_der)
except Exception:
return FalseHybrid Mode Verification
def verify_hybrid_signature(pubkey_hex: str, signature_hex: str, message: bytes) -> bool:
"""Verify a hybrid Falcon-512 + ECDSA signature.
Args:
pubkey_hex: Combined public keys as hex string (897 + 33 = 930 bytes)
signature_hex: Combined signatures as hex string
message: Message bytes to verify against
Returns:
bool: True if both signatures are valid
"""
try:
pubkey = bytes.fromhex(pubkey_hex)
signature = bytes.fromhex(signature_hex)
# Split keys and signatures
falcon_pubkey = pubkey[:897]
ecdsa_pubkey = pubkey[897:]
falcon_sig = signature[:len(signature)//2]
ecdsa_sig = signature[len(signature)//2:]
# Verify both signatures
falcon_valid = verify_falcon512_signature(
falcon_pubkey.hex(), falcon_sig.hex(), message
)
ecdsa_valid = verify_ecdsa_signature(
ecdsa_pubkey.hex(), ecdsa_sig.hex(), message
)
return falcon_valid and ecdsa_valid
except Exception:
return FalseRust Implementation
Dependencies (Cargo.toml)
[dependencies]
sha2 = "0.10"
hex = "0.4"
falcon-rust = { git = "https://github.com/your-falcon-impl" } # Custom Falcon implementation
dilithium-rust = "0.1" # ML-DSA implementationFalcon-512 Verification
use falcon_rust::{PublicKey, Signature};
use sha2::{Sha256, Digest};
fn verify_falcon512_signature(
pubkey_hex: &str,
signature_hex: &str,
message: &[u8]
) -> Result<bool, Box<dyn std::error::Error>> {
// Decode inputs
let pubkey_bytes = hex::decode(pubkey_hex)?;
let signature_bytes = hex::decode(signature_hex)?;
// Verify sizes
if pubkey_bytes.len() != 897 {
return Ok(false);
}
if !(666..=752).contains(&signature_bytes.len()) {
return Ok(false);
}
// Hash message
let mut hasher = Sha256::new();
hasher.update(message);
let digest = hasher.finalize();
// Parse public key and signature
let pubkey = PublicKey::from_bytes(&pubkey_bytes)?;
let signature = Signature::from_bytes(&signature_bytes)?;
// Verify
Ok(pubkey.verify(&digest, &signature))
}ML-DSA-44 Verification
use dilithium_rust::Dilithium;
fn verify_mldsa44_signature(
pubkey_hex: &str,
signature_hex: &str,
message: &[u8]
) -> Result<bool, Box<dyn std::error::Error>> {
// Decode inputs
let pubkey_bytes = hex::decode(pubkey_hex)?;
let signature_bytes = hex::decode(signature_hex)?;
// Verify sizes
if pubkey_bytes.len() != 1312 {
return Ok(false);
}
if signature_bytes.len() != 2420 {
return Ok(false);
}
// Create verifier
let verifier = Dilithium::new();
let public_key = verifier.public_key_from_bytes(&pubkey_bytes)?;
// Verify signature
Ok(verifier.verify(&public_key, message, &signature_bytes))
}Message Construction
Standard Message Format
For Dogenals v2.0 inscriptions, the message to sign is:
def build_message(metadata: dict) -> bytes:
"""Build the message that gets signed."""
components = [
metadata["content_hash"],
metadata["collection_id"],
metadata["owner_pubkey"],
metadata.get("parent", "")
]
message = "".join(components)
return hashlib.sha256(message.encode('utf-8')).digest()Example
metadata = {
"content_hash": "a665a45920422f9d417e4867efdc4fb8a04a1f3fff1fa07e998e86f7f7a27ae3",
"collection_id": "b613679a0814d9ec772f95d778c35fc5ff1697c493715653c6c712144292c5ad",
"owner_pubkey": "04a1f3fff1fa07e998e86f7f7a27ae3a665a45920422f9d417e4867efdc4fb8a",
"parent": "aaaa0000bbbb1111cccc2222dddd3333eeee4444ffff5555666677778888i0"
}
message = build_message(metadata)
# Sign this message with your chosen algorithmTesting
Test Vectors
# Valid Falcon-512 signature test
test_pubkey = "04a1f3..." # 897 bytes
test_signature = "b61367..." # 666 bytes
test_message = b"Hello Dogenals v2.0"
assert verify_falcon512_signature(test_pubkey, test_signature, test_message)Performance Benchmarks
- Falcon-512: ~1ms verification on modern hardware
- ML-DSA-44: ~5ms verification
- ECDSA: ~0.1ms verification (but quantum-vulnerable)
Security Considerations
Key Management
- Store private keys securely (HSM recommended)
- Never reuse keys across different collections
- Implement key rotation for long-lived collections
Signature Validation
- Always validate signature before accepting inscription
- Reject inscriptions with invalid signatures
- Log verification failures for monitoring
Quantum Migration
- Prefer Falcon-512 for new implementations
- Use hybrid mode during transition period
- Phase out ECDSA by 2030 (estimated quantum computer timeline)
Troubleshooting
Common Issues
- Wrong key size: Verify you’re using correct algorithm constants
- Invalid signature format: Ensure hex encoding is correct
- Message mismatch: Double-check message construction
- Library compatibility: Test with known good implementations
Debug Steps
# 1. Verify input sizes
print(f"Pubkey size: {len(bytes.fromhex(pubkey_hex))}")
print(f"Signature size: {len(bytes.fromhex(signature_hex))}")
# 2. Check message construction
message = build_message(metadata)
print(f"Message hash: {hashlib.sha256(message).hexdigest()}")
# 3. Test with known good values
# Use test vectors from specificationReference Implementations
- PyDoge: Python SDK with PQC verification
- wonky-dogeord: Rust indexer with signature validation
- kabosu: Go implementation with hybrid support
Remember: Quantum safety isn’t optional for Dogenals v2.0. Always verify signatures before accepting inscriptions.
// Dog’s Chosen Tech — Dogenals v2.0 — jonheaven (BC, Canada) — April 2026