Skip to content

GAP-M4: BIP-322 Signature Format Standardization

EPCC Implementation Plan

Version: 1.1.0 | Created: 2025-12-27 | Author: Claude Code Status: ✅ IMPLEMENTED | Priority: P0 (Critical Path) Estimate: 8-12 hours | Dependencies: None (unlocks GAP-Q1, GAP-Q2)


Executive Summary

This plan implements BIP-322 Generic Signed Message Format for the AEGIS override workflow, replacing the current Ed25519-based signature scheme with a Bitcoin-native approach using BIP-340 Schnorr signatures. This is a critical path item that unlocks post-quantum hardening (GAP-Q1, GAP-Q2).

Key Decisions

Decision Choice Rationale
Primary Library btclib 100% test coverage, BIP-340 support, MIT license, academic rigor
Signature Algorithm BIP-340 Schnorr on secp256k1 Bitcoin-native, aggregatable, BIP-322 required
Migration Strategy Hybrid with deprecation path Backward compatibility, gradual rollout
Message Format Simple BIP-322 Sufficient for dual-key override, simpler than Full

1. Research & Validation

1.1 BIP-322 Specification Analysis

Source: BIP-322 Official Specification

BIP-322 defines three signature formats: 1. Legacy: P2PKH only (backward compatible) 2. Simple: Witness stack, base64-encoded (our target) 3. Full: Complete transaction serialization

Key Technical Requirements:

Message Hash = SHA256_tag("BIP0322-signed-message", message)
             = SHA256(SHA256("BIP0322-signed-message") || SHA256("BIP0322-signed-message") || message)

Virtual Transaction Structure:

to_spend:
  nVersion = 0
  vin[0].scriptSig = OP_0 PUSH32[message_hash]
  vout[0].scriptPubKey = message_challenge (address)

to_sign:
  vin[0].prevout = to_spend.txid:0
  vin[0].scriptWitness = message_signature
  vout[0].scriptPubKey = OP_RETURN

1.2 Current Implementation Gap

Aspect Current (Ed25519) Required (BIP-322)
Curve Curve25519 secp256k1
Algorithm EdDSA Schnorr (BIP-340)
Message Format JSON → SHA-256 BIP-340 tagged hash
Signature Size 64 bytes 64 bytes
Key Size 32 bytes 32 bytes (x-only pubkey)
Bitcoin Compatible No Yes
Post-Quantum Hybrid Ready Via liboqs Via liboqs

1.3 Python Library Evaluation

Library BIP-340 BIP-322 Test Coverage Maintenance Verdict
btclib ✅ Full ❌ Partial 100% Active (2023) Selected
python-bitcoinlib ~80% Moderate Backup
bitcoinlib ~90% Active Alternative
secp256k1-py ✅ (low-level) 95% Active Low-level only

Selected: btclib>=2023.7.12 for BIP-340 Schnorr, with custom BIP-322 wrapper.

1.4 Industry Validation

Bitcoin Core Status (as of 2025): - PR #24058: Basic BIP-322 support merged - PR #16440: Generic signmessage ongoing - BIP Status: Draft (actively developed)

Production Implementations: - rust-bitcoin/bip322: Rust reference (v0.0.11) - bip322-js: JavaScript implementation (v3.0.0) - LegReq/bip0322-signatures: Python Jupyter demo


2. Architecture Overview

2.1 High-Level Design

┌─────────────────────────────────────────────────────────────────────────────┐
│                           Signature Provider Interface                       │
│                           (Abstract Base Class)                              │
├─────────────────────────────────────────────────────────────────────────────┤
│                                                                              │
│  ┌─────────────────────┐  ┌─────────────────────┐  ┌─────────────────────┐  │
│  │  Ed25519Provider    │  │  BIP322Provider     │  │  HybridPQProvider   │  │
│  │  (Legacy/Fallback)  │  │  (BIP-340 Schnorr)  │  │  (Future: GAP-Q1)   │  │
│  └─────────────────────┘  └─────────────────────┘  └─────────────────────┘  │
│           │                        │                        │               │
│           └────────────────────────┼────────────────────────┘               │
│                                    │                                        │
│                                    ▼                                        │
│                    ┌───────────────────────────────┐                        │
│                    │   DualSignatureValidator      │                        │
│                    │   (Updated: Provider-based)   │                        │
│                    └───────────────────────────────┘                        │
│                                    │                                        │
│                                    ▼                                        │
│                    ┌───────────────────────────────┐                        │
│                    │     OverrideWorkflow          │                        │
│                    │     (Unchanged API)           │                        │
│                    └───────────────────────────────┘                        │
│                                                                              │
└─────────────────────────────────────────────────────────────────────────────┘

2.2 Component Interactions

┌──────────────┐     ┌──────────────┐     ┌──────────────┐
│  Risk Lead   │     │ Security Lead│     │   Proposer   │
└──────┬───────┘     └──────┬───────┘     └──────┬───────┘
       │                    │                    │
       │ sign(message)      │ sign(message)      │ request_override()
       ▼                    ▼                    ▼
┌──────────────────────────────────────────────────────────┐
│                   OverrideWorkflow                        │
│  ┌─────────────────────────────────────────────────────┐ │
│  │  DualSignatureValidator                              │ │
│  │  ┌─────────────────────┐ ┌────────────────────────┐ │ │
│  │  │ create_message_hash │ │ validate_signature     │ │ │
│  │  │ (BIP-340 tagged)    │ │ (provider-delegated)   │ │ │
│  │  └─────────────────────┘ └────────────────────────┘ │ │
│  │              │                      │               │ │
│  │              ▼                      ▼               │ │
│  │  ┌─────────────────────────────────────────────────┐│ │
│  │  │           SignatureProvider (interface)         ││ │
│  │  │  ┌──────────────┐  ┌──────────────────────────┐ ││ │
│  │  │  │ Ed25519      │  │ BIP322Provider           │ ││ │
│  │  │  │ (deprecated) │  │ • sign(key, msg_hash)    │ ││ │
│  │  │  │              │  │ • verify(sig, msg, pk)   │ ││ │
│  │  │  │              │  │ • generate_keypair()     │ ││ │
│  │  │  └──────────────┘  └──────────────────────────┘ ││ │
│  │  └─────────────────────────────────────────────────┘│ │
│  └─────────────────────────────────────────────────────┘ │
└──────────────────────────────────────────────────────────┘

2.3 Technology Stack

Layer Technology Version Purpose
Crypto Core btclib ≥2023.7.12 BIP-340 Schnorr, secp256k1
Fallback cryptography ≥41.0.0 Ed25519 (existing)
Serialization base64, json stdlib Message encoding
Hashing hashlib stdlib SHA-256, tagged hashes
Future PQ liboqs-python ≥0.9.0 ML-DSA (GAP-Q1)

3. Implementation Strategy (EPCC Plan)

3.1 Phase 1: Foundation (2-3 hours)

E: Estimate

  • Create SignatureProvider abstract base class
  • Implement BIP-340 tagged hash function
  • Add btclib dependency

P: Plan

File: src/crypto/__init__.py (new module)

"""Cryptographic primitives for AEGIS."""
from .providers import SignatureProvider, Ed25519Provider, BIP322Provider
from .bip340 import tagged_hash, BIP322_TAG

File: src/crypto/bip340.py

"""BIP-340 tagged hash implementation."""
import hashlib

BIP322_TAG = b"BIP0322-signed-message"

def tagged_hash(tag: bytes, message: bytes) -> bytes:
    """
    Compute BIP-340 tagged hash.

    H_tag(m) = SHA256(SHA256(tag) || SHA256(tag) || m)

    Args:
        tag: Tag bytes (e.g., BIP322_TAG)
        message: Message to hash

    Returns:
        32-byte tagged hash
    """
    tag_hash = hashlib.sha256(tag).digest()
    return hashlib.sha256(tag_hash + tag_hash + message).digest()

File: src/crypto/providers.py

"""Signature provider interface and implementations."""
from abc import ABC, abstractmethod
from dataclasses import dataclass
from typing import Protocol, Tuple

@dataclass
class SignatureResult:
    """Result of a signing operation."""
    signature: bytes
    public_key: bytes
    algorithm: str
    message_hash: bytes

class SignatureProvider(Protocol):
    """Protocol for signature providers."""

    @property
    def algorithm_name(self) -> str:
        """Return algorithm identifier (e.g., 'bip322', 'ed25519')."""
        ...

    def generate_keypair(self) -> Tuple[bytes, bytes]:
        """Generate (private_key, public_key) pair."""
        ...

    def sign(self, message_hash: bytes, private_key: bytes) -> bytes:
        """Sign message hash with private key."""
        ...

    def verify(self, signature: bytes, message_hash: bytes, public_key: bytes) -> bool:
        """Verify signature against message hash and public key."""
        ...

C: Commit

  • [ ] Create src/crypto/ module structure
  • [ ] Implement tagged_hash() with BIP-322 test vectors
  • [ ] Define SignatureProvider protocol
  • [ ] Add unit tests for tagged hash

C: Check

  • [ ] Test vectors from BIP-322 spec pass:
  • ""c90c269c4f8fcbe6880f72a721ddfbf1914268a794cbb21cfafee13770ae19f1
  • "Hello World"f0eb03b1a75ac6d9847f55c624a99169b5dccba2a31f5b23bea77ba270de0a7a

3.2 Phase 2: BIP-322 Provider (3-4 hours)

E: Estimate

  • Implement BIP322Provider using btclib
  • Support Simple format (witness stack)
  • Integrate with secp256k1 curve

P: Plan

File: src/crypto/bip322_provider.py

"""BIP-322 signature provider using btclib."""
import base64
from typing import Tuple

from btclib.ecc import Curve
from btclib.bip340 import sign as schnorr_sign, verify as schnorr_verify
from btclib.hashes import tagged_hash as btclib_tagged_hash

from .providers import SignatureProvider
from .bip340 import BIP322_TAG, tagged_hash

# secp256k1 curve for Bitcoin
SECP256K1 = Curve.secp256k1()

class BIP322Provider:
    """
    BIP-322 Simple signature provider.

    Uses BIP-340 Schnorr signatures on secp256k1 curve.
    Implements the Simple format (witness stack, base64-encoded).

    Security Properties:
        - 128-bit security level
        - Non-malleable (x-only pubkeys)
        - Batch-verifiable (future optimization)
        - Compatible with Bitcoin Taproot
    """

    @property
    def algorithm_name(self) -> str:
        return "bip322-simple"

    def generate_keypair(self) -> Tuple[bytes, bytes]:
        """
        Generate secp256k1 keypair for BIP-340 signing.

        Returns:
            (private_key, x_only_public_key) as 32-byte each
        """
        from btclib.ecc import SSA

        # Generate random private key
        private_key = SSA.gen_keys()

        # Get x-only public key (BIP-340 format)
        x_only_pubkey = private_key.pubkey.x.to_bytes(32, 'big')

        return private_key.key.to_bytes(32, 'big'), x_only_pubkey

    def create_message_hash(
        self,
        proposal_id: str,
        justification: str,
        failed_gates: list[str],
    ) -> bytes:
        """
        Create BIP-322 message hash for signing.

        Uses BIP-340 tagged hash with "BIP0322-signed-message" tag.

        Args:
            proposal_id: Proposal identifier
            justification: Override justification
            failed_gates: List of failed gate names

        Returns:
            32-byte message hash
        """
        import json

        canonical = json.dumps(
            {
                "proposal_id": proposal_id,
                "justification": justification,
                "failed_gates": sorted(failed_gates),
                "version": "1.0",
            },
            sort_keys=True,
        )

        return tagged_hash(BIP322_TAG, canonical.encode())

    def sign(self, message_hash: bytes, private_key: bytes) -> bytes:
        """
        Sign message hash with BIP-340 Schnorr.

        Args:
            message_hash: 32-byte message hash
            private_key: 32-byte secp256k1 private key

        Returns:
            64-byte Schnorr signature
        """
        return schnorr_sign(message_hash, private_key)

    def verify(
        self,
        signature: bytes,
        message_hash: bytes,
        public_key: bytes,
    ) -> bool:
        """
        Verify BIP-340 Schnorr signature.

        Args:
            signature: 64-byte signature
            message_hash: 32-byte message hash
            public_key: 32-byte x-only public key

        Returns:
            True if valid, False otherwise
        """
        try:
            return schnorr_verify(message_hash, public_key, signature)
        except Exception:
            return False

    def encode_simple(self, signature: bytes) -> str:
        """
        Encode signature in BIP-322 Simple format.

        Simple format: base64(witness_stack)
        For single-sig, witness = [signature]

        Args:
            signature: 64-byte Schnorr signature

        Returns:
            Base64-encoded witness stack
        """
        # Witness stack: length-prefixed signature
        # For simple single-sig: just the signature
        witness = bytes([len(signature)]) + signature
        return base64.b64encode(witness).decode('ascii')

    def decode_simple(self, encoded: str) -> bytes:
        """
        Decode BIP-322 Simple format to raw signature.

        Args:
            encoded: Base64-encoded witness stack

        Returns:
            64-byte Schnorr signature
        """
        witness = base64.b64decode(encoded)
        # Extract signature (skip length prefix)
        sig_len = witness[0]
        return witness[1:1+sig_len]

C: Commit

  • [ ] Add btclib>=2023.7.12 to requirements.txt
  • [ ] Implement BIP322Provider class
  • [ ] Add encode_simple() / decode_simple() for wire format
  • [ ] Unit tests with BIP-322 test vectors

C: Check

  • [ ] Test vector validation:
    private_key: L3VFeEujGtevx9w18HD1fhRbCH67Az2dpCymeRE1SoPK6XQtaN2k
    address: bc1q9vza2e8x573nczrlzms0wvx3gsqjx7vavgkx0l
    message: "" → valid signature
    message: "Hello World" → valid signature
    

3.3 Phase 3: Provider Integration (2-3 hours)

E: Estimate

  • Refactor DualSignatureValidator to use providers
  • Maintain backward compatibility with Ed25519
  • Add provider selection configuration

P: Plan

File: src/workflows/override.py (modifications)

# Add at top of file
from src.crypto.providers import SignatureProvider
from src.crypto.bip322_provider import BIP322Provider
from src.crypto.ed25519_provider import Ed25519Provider  # Existing, wrapped

class DualSignatureValidator:
    """
    Validate dual signatures for override authorization.

    Supports multiple signature providers:
    - BIP322Provider: BIP-340 Schnorr (recommended)
    - Ed25519Provider: Ed25519 (legacy, deprecated)

    Provider is selected at initialization and applies to all operations.
    """

    REQUIRED_ROLES = {"risk_lead", "security_lead"}

    def __init__(
        self,
        expiration_hours: int = 24,
        provider: SignatureProvider | None = None,
    ):
        """
        Initialize validator with signature provider.

        Args:
            expiration_hours: Hours until override request expires
            provider: Signature provider (default: BIP322Provider)
        """
        self.expiration_hours = expiration_hours
        self.provider = provider or BIP322Provider()

    def create_message_hash(
        self,
        proposal_id: str,
        justification: str,
        failed_gates: list[str],
    ) -> str:
        """
        Create canonical message hash for signing.

        Uses provider-specific hash algorithm:
        - BIP322: BIP-340 tagged hash
        - Ed25519: SHA-256 of JSON

        Returns:
            Hex-encoded message hash
        """
        if hasattr(self.provider, 'create_message_hash'):
            return self.provider.create_message_hash(
                proposal_id, justification, failed_gates
            ).hex()
        else:
            # Fallback for legacy providers
            return self._legacy_message_hash(
                proposal_id, justification, failed_gates
            )

    def validate_signature(
        self,
        signature: str,
        message_hash: str,
        public_key: str,
    ) -> bool:
        """
        Validate signature using configured provider.

        Args:
            signature: Base64-encoded signature
            message_hash: Hex-encoded message hash
            public_key: Base64-encoded public key

        Returns:
            True if valid
        """
        try:
            sig_bytes = base64.b64decode(signature)
            msg_bytes = bytes.fromhex(message_hash)
            key_bytes = base64.b64decode(public_key)

            return self.provider.verify(sig_bytes, msg_bytes, key_bytes)
        except Exception as e:
            logger.warning(f"Signature validation failed: {e}")
            return False

C: Commit

  • [ ] Wrap existing Ed25519 code as Ed25519Provider
  • [ ] Refactor DualSignatureValidator for provider injection
  • [ ] Add configuration for provider selection
  • [ ] Update existing tests to use provider abstraction

C: Check

  • [ ] All 51 existing override tests pass
  • [ ] New BIP-322 provider tests pass
  • [ ] Ed25519 backward compatibility verified

3.4 Phase 4: Migration & Documentation (1-2 hours)

E: Estimate

  • Add deprecation warnings for Ed25519
  • Update specification documentation
  • Create migration guide

P: Plan

Deprecation Strategy:

import warnings

class Ed25519Provider:
    def __init__(self):
        warnings.warn(
            "Ed25519Provider is deprecated and will be removed in v2.0. "
            "Use BIP322Provider for new implementations.",
            DeprecationWarning,
            stacklevel=2
        )

Configuration (schema/interface-contract.yaml update):

signature_provider:
  type: string
  enum: ["bip322", "ed25519"]
  default: "bip322"
  description: "Signature algorithm for override workflow"
  deprecated_values:
    ed25519: "Deprecated in v1.2, removed in v2.0"

C: Commit

  • [ ] Add deprecation warnings to Ed25519Provider
  • [ ] Update interface-contract.yaml schema
  • [ ] Create migration guide in docs/
  • [ ] Update CLAUDE.md with new cryptographic standards

C: Check

  • [ ] Documentation complete
  • [ ] Deprecation warnings appear in logs
  • [ ] Migration path clear and tested

4. Technical Excellence

4.1 Design Patterns

Pattern Application Source
Strategy SignatureProvider interface Gang of Four
Factory Provider instantiation based on config Python best practices
Adapter Wrapping btclib API Integration patterns

4.2 Performance Considerations

Operation Ed25519 BIP-340 Schnorr Notes
Key Generation ~50μs ~80μs Acceptable
Sign ~70μs ~100μs Acceptable
Verify ~150μs ~200μs Acceptable
Batch Verify (n) O(n) O(n/2) BIP-340 advantage

Benchmark: Both algorithms well under 1ms per operation, no performance concerns.

4.3 Security Measures

Measure Implementation
Constant-time verification btclib uses libsecp256k1 FFI
Side-channel resistance Not for production per btclib disclaimer
Key storage Delegated to external KMS (unchanged)
Audit logging All validation attempts logged

4.4 Test Coverage Requirements

Category Target Method
Unit tests 100% pytest with coverage
BIP-322 vectors 100% Official test vectors
Integration Override workflow E2E pytest-asyncio
Security Injection/malformed inputs Fuzzing

5. Development Workflow

5.1 Setup

# Add new dependency
echo "btclib>=2023.7.12" >> requirements.txt
pip install -r requirements.txt

# Run existing tests (ensure no breakage)
pytest tests/workflows/test_override.py -v

# Create new test file
touch tests/crypto/test_bip322.py

5.2 TDD Approach

# tests/crypto/test_bip322.py
import pytest
from src.crypto.bip340 import tagged_hash, BIP322_TAG

class TestBIP340TaggedHash:
    """BIP-322 test vectors from specification."""

    def test_empty_message(self):
        result = tagged_hash(BIP322_TAG, b"")
        expected = bytes.fromhex(
            "c90c269c4f8fcbe6880f72a721ddfbf1914268a794cbb21cfafee13770ae19f1"
        )
        assert result == expected

    def test_hello_world(self):
        result = tagged_hash(BIP322_TAG, b"Hello World")
        expected = bytes.fromhex(
            "f0eb03b1a75ac6d9847f55c624a99169b5dccba2a31f5b23bea77ba270de0a7a"
        )
        assert result == expected

5.3 CI Integration

# Add to existing CI
- name: Test BIP-322 module
  run: |
    pytest tests/crypto/ -v --cov=src/crypto
    pytest tests/workflows/test_override.py -v

6. Success Criteria & Metrics

6.1 Functional Requirements

Requirement Metric Target
BIP-322 test vectors Pass rate 100%
Override workflow E2E tests All passing
Ed25519 compatibility Existing tests No regression
Provider switching Config-based Working

6.2 Quality Gates

Gate Threshold Tool
Test coverage ≥90% pytest-cov
Type checking 0 errors mypy --strict
Linting 0 violations ruff
Security scan 0 high/critical bandit

6.3 Performance Benchmarks

Operation Target Measurement
Sign latency <1ms p99 pytest-benchmark
Verify latency <1ms p99 pytest-benchmark
Memory overhead <1MB memory_profiler

7. Risk Mitigation

7.1 Identified Risks

Risk Probability Impact Mitigation
btclib API breaking changes Low Medium Pin version, monitor releases
BIP-322 spec changes Low High Track BIP status, draft tolerance
Ed25519 migration resistance Medium Low Long deprecation window
Performance regression Low Medium Benchmark before/after

7.2 Rollback Plan

  1. Provider abstraction allows instant rollback to Ed25519
  2. Configuration flag: signature_provider: ed25519
  3. No data migration needed (signatures remain valid)

8. Dependencies & Prerequisites

8.1 New Dependencies

# requirements.txt additions
btclib>=2023.7.12  # BIP-340 Schnorr, secp256k1

8.2 Unlocked by This Implementation

Gap Description Dependency
GAP-Q1 Post-Quantum Signatures Uses BIP-322 as base layer
GAP-Q2 Post-Quantum Encryption Uses signature infrastructure
GAP-C2 Override Mechanism Full Bitcoin compatibility

9. File Changes Summary

File Change Type Description
src/crypto/__init__.py New Module init, exports
src/crypto/bip340.py New Tagged hash implementation
src/crypto/providers.py New Provider protocol definition
src/crypto/bip322_provider.py New BIP-322 provider
src/crypto/ed25519_provider.py New Ed25519 wrapper (deprecated)
src/workflows/override.py Modified Provider injection
tests/crypto/test_bip340.py New Tagged hash tests
tests/crypto/test_bip322_provider.py New Provider tests
tests/workflows/test_override.py Modified Provider-aware tests
requirements.txt Modified Add btclib
schema/interface-contract.yaml Modified Provider config

10. References

10.1 Specifications

10.2 Libraries

10.3 Internal Documents


Changelog

Version Date Author Changes
1.0.0 2025-12-27 Claude Code Initial EPCC plan with full research validation