# Algorithm Management

Cryptographic algorithm registration and management system providing custom algorithm support, algorithm whitelisting for security, and flexible algorithm configuration for both global and instance-level operations.

## Capabilities

### Global Algorithm Functions

Global functions for managing algorithms across all PyJWT operations using a shared algorithm registry.

```python { .api }
def register_algorithm(alg_id: str, alg_obj) -> None:
    """
    Register a custom algorithm globally for all JWT/JWS operations.

    Args:
        alg_id (str): Algorithm identifier (e.g., 'CUSTOM256')
        alg_obj: Algorithm implementation object

    Raises:
        ValueError: Algorithm already registered
        TypeError: Invalid algorithm object
    """

def unregister_algorithm(alg_id: str) -> None:
    """
    Remove an algorithm from the global registry.

    Args:
        alg_id (str): Algorithm identifier to remove

    Raises:
        KeyError: Algorithm not registered
    """

def get_algorithm_by_name(alg_name: str):
    """
    Retrieve algorithm implementation by name from global registry.

    Args:
        alg_name (str): Algorithm name

    Returns:
        Algorithm object implementation

    Raises:
        NotImplementedError: Algorithm not supported or cryptography missing
    """

def get_unverified_header(jwt: str | bytes) -> dict:
    """
    Extract JWT header without signature verification.

    Args:
        jwt (str | bytes): JWT token

    Returns:
        dict: JWT header dictionary

    Raises:
        DecodeError: Invalid token format
        InvalidTokenError: Invalid header structure
    """
```

Usage examples:

```python
import jwt
from jwt.algorithms import Algorithm

# Check algorithm availability
try:
    alg = jwt.get_algorithm_by_name('RS256')
    print(f"RS256 available: {alg}")
except NotImplementedError as e:
    print(f"RS256 not available: {e}")

# Extract header for algorithm info
token = "eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiJ9..."
header = jwt.get_unverified_header(token)
print(f"Token algorithm: {header['alg']}")

# Register custom algorithm globally
class CustomHMAC(Algorithm):
    def sign(self, msg, key):
        # Custom signing implementation
        import hashlib
        import hmac
        return hmac.new(key, msg, hashlib.sha3_256).digest()
    
    def verify(self, msg, key, sig):
        # Custom verification implementation
        expected = self.sign(msg, key)
        return hmac.compare_digest(sig, expected)

jwt.register_algorithm('CUSTOM-HMAC', CustomHMAC())

# Use custom algorithm
payload = {'user_id': 123}
token = jwt.encode(payload, b'secret', algorithm='CUSTOM-HMAC')
decoded = jwt.decode(token, b'secret', algorithms=['CUSTOM-HMAC'])

# Remove algorithm when no longer needed
jwt.unregister_algorithm('CUSTOM-HMAC')
```

### Instance-Level Algorithm Management

Algorithm management for specific PyJWT and PyJWS instances with custom algorithm sets and whitelisting.

```python { .api }
# PyJWS instance methods
class PyJWS:
    def register_algorithm(self, alg_id: str, alg_obj) -> None:
        """Register algorithm for this JWS instance only."""
    
    def unregister_algorithm(self, alg_id: str) -> None:
        """Unregister algorithm from this JWS instance."""
    
    def get_algorithm_by_name(self, alg_name: str):
        """Get algorithm from this JWS instance."""
    
    def get_algorithms(self) -> list:
        """List algorithms available to this JWS instance."""

# PyJWT inherits algorithm management through its internal PyJWS instance
```

Usage examples:

```python
import jwt

# Create JWS instance with limited algorithms
jws = jwt.PyJWS(algorithms=['HS256', 'HS512'])  # Only allow HMAC
print(f"Allowed algorithms: {jws.get_algorithms()}")

# Add algorithm to specific instance
custom_alg = CustomHMAC()
jws.register_algorithm('CUSTOM', custom_alg)

# This instance now supports CUSTOM, but global instance doesn't
token = jws.encode(b'payload', b'key', algorithm='CUSTOM')

# Different instance with different algorithms
secure_jws = jwt.PyJWS(algorithms=['RS256', 'ES256'])  # Only asymmetric
secure_jws.register_algorithm('CUSTOM-RSA', CustomRSA())
```

### Algorithm Security and Whitelisting

Security-focused algorithm management with explicit whitelisting and algorithm restrictions.

```python
import jwt

# Secure algorithm whitelisting
ALLOWED_ALGORITHMS = ['RS256', 'ES256', 'EdDSA']  # Only strong asymmetric

def verify_secure_token(token, key):
    """Verify token with strict algorithm policy."""
    return jwt.decode(token, key, algorithms=ALLOWED_ALGORITHMS)

# Instance with security policy
secure_jwt = jwt.PyJWT()
secure_jws = jwt.PyJWS(algorithms=ALLOWED_ALGORITHMS)

# Block weak algorithms
weak_algorithms = ['none', 'HS256']  # Example: block 'none' and symmetric
for alg in weak_algorithms:
    try:
        jwt.unregister_algorithm(alg)
    except KeyError:
        pass  # Algorithm not registered

# Verify no weak algorithms remain
available = jwt.PyJWS().get_algorithms()
dangerous = set(available) & set(weak_algorithms)
if dangerous:
    print(f"Warning: Dangerous algorithms still available: {dangerous}")
```

### Algorithm Implementation Interface

Base interface for implementing custom algorithms compatible with PyJWT.

```python { .api }
class Algorithm:
    """
    Base class for all algorithm implementations.
    
    Custom algorithms must inherit from this class and implement
    required methods for signing and verification.
    """
    
    def sign(self, msg: bytes, key) -> bytes:
        """
        Sign message with the given key.
        
        Args:
            msg (bytes): Message to sign
            key: Cryptographic key for signing
            
        Returns:
            bytes: Signature
        """
        raise NotImplementedError()
    
    def verify(self, msg: bytes, key, sig: bytes) -> bool:
        """
        Verify signature against message and key.
        
        Args:
            msg (bytes): Original message
            key: Cryptographic key for verification
            sig (bytes): Signature to verify
            
        Returns:
            bool: True if signature is valid
        """
        raise NotImplementedError()
    
    def prepare_key(self, key):
        """
        Prepare and validate key for use with this algorithm.
        
        Args:
            key: Raw key material
            
        Returns:
            Prepared key object
            
        Raises:
            InvalidKeyError: Key is invalid for this algorithm
        """
        return key
```

### Custom Algorithm Examples

Examples of implementing custom algorithms for specific use cases:

#### Custom HMAC with Different Hash Function

```python
import hashlib
import hmac
from jwt.algorithms import Algorithm

class HMACSHA3_256(Algorithm):
    """HMAC using SHA3-256 hash function."""
    
    def sign(self, msg: bytes, key: bytes) -> bytes:
        return hmac.new(key, msg, hashlib.sha3_256).digest()
    
    def verify(self, msg: bytes, key: bytes, sig: bytes) -> bool:
        expected = self.sign(msg, key)
        return hmac.compare_digest(sig, expected)
    
    def prepare_key(self, key):
        if not isinstance(key, (bytes, str)):
            raise jwt.InvalidKeyError("HMAC key must be bytes or string")
        if isinstance(key, str):
            key = key.encode('utf-8')
        return key

# Register and use
jwt.register_algorithm('HS3-256', HMACSHA3_256())
token = jwt.encode({'data': 'test'}, 'secret', algorithm='HS3-256')
```

#### Custom Algorithm with Key Validation

```python
from jwt.algorithms import Algorithm
from jwt.exceptions import InvalidKeyError

class SecureHMAC(Algorithm):
    """HMAC with minimum key length requirement."""
    
    MIN_KEY_LENGTH = 32  # Require 256-bit keys minimum
    
    def prepare_key(self, key):
        if isinstance(key, str):
            key = key.encode('utf-8')
        
        if not isinstance(key, bytes):
            raise InvalidKeyError("Key must be bytes or string")
        
        if len(key) < self.MIN_KEY_LENGTH:
            raise InvalidKeyError(f"Key must be at least {self.MIN_KEY_LENGTH} bytes")
        
        return key
    
    def sign(self, msg: bytes, key: bytes) -> bytes:
        return hmac.new(key, msg, hashlib.sha256).digest()
    
    def verify(self, msg: bytes, key: bytes, sig: bytes) -> bool:
        expected = self.sign(msg, key)
        return hmac.compare_digest(sig, expected)

# Usage with key validation
jwt.register_algorithm('SECURE-HMAC', SecureHMAC())

try:
    # This will fail - key too short
    jwt.encode({'data': 'test'}, 'short', algorithm='SECURE-HMAC')
except InvalidKeyError as e:
    print(f"Key rejected: {e}")

# This will work - key meets minimum length
long_key = 'a' * 32
token = jwt.encode({'data': 'test'}, long_key, algorithm='SECURE-HMAC')
```

### Algorithm Discovery and Introspection

Tools for discovering available algorithms and their capabilities:

```python
import jwt

def list_available_algorithms():
    """List all globally available algorithms."""
    jws = jwt.PyJWS()
    algorithms = jws.get_algorithms()
    
    for alg_name in sorted(algorithms):
        try:
            alg_obj = jwt.get_algorithm_by_name(alg_name)
            print(f"{alg_name}: {type(alg_obj).__name__}")
        except NotImplementedError as e:
            print(f"{alg_name}: Not available ({e})")

def check_cryptography_algorithms():
    """Check which algorithms require cryptography library."""
    from jwt.algorithms import requires_cryptography, has_crypto
    
    print(f"Cryptography installed: {has_crypto}")
    print(f"Algorithms requiring cryptography: {requires_cryptography}")
    
    if not has_crypto:
        print("Install cryptography for full algorithm support:")
        print("pip install PyJWT[crypto]")

# Run diagnostics
list_available_algorithms()
check_cryptography_algorithms()
```

### Security Best Practices

Recommended patterns for secure algorithm management:

```python
import jwt

# 1. Use explicit algorithm whitelists
PRODUCTION_ALGORITHMS = ['RS256', 'ES256', 'EdDSA']

def secure_decode(token, key):
    return jwt.decode(token, key, algorithms=PRODUCTION_ALGORITHMS)

# 2. Block dangerous algorithms
BLOCKED_ALGORITHMS = ['none', 'HS256']  # Example policy

for alg in BLOCKED_ALGORITHMS:
    try:
        jwt.unregister_algorithm(alg)
    except KeyError:
        pass

# 3. Use asymmetric algorithms in production
def create_production_jwt_instance():
    return jwt.PyJWT(algorithms=['RS256', 'ES256'])

# 4. Validate algorithm in token matches expected
def verify_with_algorithm_check(token, key, expected_algorithm):
    header = jwt.get_unverified_header(token)
    if header.get('alg') != expected_algorithm:
        raise ValueError(f"Unexpected algorithm: {header.get('alg')}")
    
    return jwt.decode(token, key, algorithms=[expected_algorithm])

# 5. Regular algorithm audit
def audit_algorithms():
    """Audit currently available algorithms for security compliance."""
    available = jwt.PyJWS().get_algorithms()
    
    dangerous = {'none'}  # Example dangerous algorithms
    weak = {'HS256', 'HS384', 'HS512'}  # Example: symmetric keys
    
    found_dangerous = set(available) & dangerous
    found_weak = set(available) & weak
    
    if found_dangerous:
        print(f"CRITICAL: Dangerous algorithms available: {found_dangerous}")
    if found_weak:
        print(f"WARNING: Weak algorithms available: {found_weak}")
    
    return len(found_dangerous) == 0 and len(found_weak) == 0

# Run security audit
if not audit_algorithms():
    print("Algorithm security policy violations detected!")
```