CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/pypi-josepy

JOSE protocol implementation in Python with support for JSON Web Algorithms, Keys, and Signatures

73

1.15x
Overview
Eval results
Files

utilities.mddocs/

Base64 and JSON Utilities

JOSE-compliant base64 encoding/decoding and JSON serialization utilities with support for certificates, CSRs, and typed field handling. Provides the foundation for secure data encoding and structured JSON object management.

Capabilities

JOSE Base64 Encoding

URL-safe base64 encoding with padding stripped according to JOSE specifications.

def b64encode(data: bytes) -> bytes:
    """
    JOSE Base64 encode.
    
    Parameters:
    - data: Data to be encoded
    
    Returns:
    bytes: JOSE Base64 string (URL-safe, no padding)
    
    Raises:
    TypeError: If data is not bytes
    """

def b64decode(data: Union[bytes, str]) -> bytes:
    """
    JOSE Base64 decode.
    
    Parameters:
    - data: Base64 string to decode (str or bytes)
    
    Returns:
    bytes: Decoded data
    
    Raises:
    TypeError: If input is not str or bytes
    ValueError: If input contains non-ASCII characters (str input)
    """

Usage Examples

from josepy import b64encode, b64decode

# Encode data to JOSE base64
data = b"Hello, JOSE Base64!"
encoded = b64encode(data)
print(f"Encoded: {encoded}")  # URL-safe, no padding

# Decode back to original
decoded = b64decode(encoded)
print(f"Decoded: {decoded}")

# Decode from string
encoded_str = encoded.decode('ascii')
decoded_from_str = b64decode(encoded_str)
assert decoded == decoded_from_str

# JOSE base64 vs standard base64
import base64
standard_b64 = base64.b64encode(data)
jose_b64 = b64encode(data)
print(f"Standard: {standard_b64}")  # May have padding and +/
print(f"JOSE:     {jose_b64}")      # URL-safe, no padding

Extended JOSE Base64 Utilities

Additional encoding/decoding functions for JOSE-specific data handling.

def encode_b64jose(data: bytes) -> str:
    """
    Encode bytes to JOSE base64 string.
    
    Parameters:
    - data: Data to encode
    
    Returns:
    str: JOSE base64 encoded string
    """

def decode_b64jose(data: str, size: Optional[int] = None, minimum: bool = False) -> bytes:
    """
    Decode JOSE base64 string to bytes with size validation.
    
    Parameters:
    - data: JOSE base64 encoded string
    - size: Expected output size in bytes (optional)
    - minimum: If True, size is minimum required (default: exact match)
    
    Returns:
    bytes: Decoded data
    
    Raises:
    josepy.errors.DeserializationError: If size validation fails
    """

def encode_hex16(value: bytes) -> str:
    """
    Encode bytes to hex string.
    
    Parameters:
    - value: Bytes to encode
    
    Returns:
    str: Hex encoded string
    """

def decode_hex16(value: str, size: Optional[int] = None, minimum: bool = False) -> bytes:
    """
    Decode hex string to bytes with size validation.
    
    Parameters:
    - value: Hex encoded string
    - size: Expected output size in bytes (optional)
    - minimum: If True, size is minimum required
    
    Returns:
    bytes: Decoded bytes
    """

X.509 Certificate Utilities

Utilities for encoding and decoding X.509 certificates and Certificate Signing Requests (CSRs).

def encode_cert(cert: x509.Certificate) -> str:
    """
    Encode X.509 certificate to base64 DER string.
    
    Parameters:
    - cert: X.509 certificate object from cryptography
    
    Returns:
    str: Base64 DER encoded certificate
    """

def decode_cert(b64der: str) -> x509.Certificate:
    """
    Decode base64 DER string to X.509 certificate.
    
    Parameters:
    - b64der: Base64 DER encoded certificate string
    
    Returns:
    x509.Certificate: Certificate object
    
    Raises:
    josepy.errors.DeserializationError: If decoding fails
    """

def encode_csr(csr: x509.CertificateSigningRequest) -> str:
    """
    Encode X.509 CSR to base64 DER string.
    
    Parameters:
    - csr: X.509 CSR object from cryptography
    
    Returns:
    str: Base64 DER encoded CSR
    """

def decode_csr(b64der: str) -> x509.CertificateSigningRequest:
    """
    Decode base64 DER string to X.509 CSR.
    
    Parameters:
    - b64der: Base64 DER encoded CSR string
    
    Returns:
    x509.CertificateSigningRequest: CSR object
    
    Raises:
    josepy.errors.DeserializationError: If decoding fails
    """

Usage Examples

from josepy import encode_cert, decode_cert, encode_csr, decode_csr
from cryptography import x509
from cryptography.x509.oid import NameOID
from cryptography.hazmat.primitives import hashes

# Assuming you have a certificate
# cert = x509.load_pem_x509_certificate(cert_pem_data, default_backend())

# Encode certificate to base64 DER
cert_b64 = encode_cert(cert)
print(f"Certificate B64: {cert_b64[:60]}...")

# Decode back to certificate object
decoded_cert = decode_cert(cert_b64)
assert cert == decoded_cert

# Same process for CSRs
# csr = x509.load_pem_x509_csr(csr_pem_data, default_backend())
csr_b64 = encode_csr(csr)
decoded_csr = decode_csr(csr_b64)

JSON Object Framework

Framework for creating JSON-serializable objects with typed fields and validation.

def field(json_name: str, default: Any = None, omitempty: bool = False, decoder: Optional[Callable] = None, encoder: Optional[Callable] = None) -> Any:
    """
    Declare a JSON field with type annotations and custom encoding/decoding.
    
    Parameters:
    - json_name: JSON property name
    - default: Default value if omitted
    - omitempty: Skip field if value equals default
    - decoder: Function to decode from JSON value
    - encoder: Function to encode to JSON value
    
    Returns:
    Field descriptor for use in class definitions
    """

class Field:
    """Field descriptor for JSON object properties"""
    
    def __init__(self, json_name: str, default: Any = None, omitempty: bool = False, decoder: Optional[Callable] = None, encoder: Optional[Callable] = None): ...
    
    def decode(self, value: Any) -> Any:
        """Decode JSON value to Python object"""
    
    def encode(self, value: Any) -> Any:
        """Encode Python object to JSON value"""

class JSONObjectWithFields:
    """Base class for JSON objects with field definitions"""
    
    @classmethod
    def from_json(cls, jobj: Any): ...
    
    def to_partial_json(self) -> Any: ...
    
    def json_dumps(self, **kwargs) -> str: ...
    
    @classmethod
    def json_loads(cls, json_string: str): ...

class TypedJSONObjectWithFields(JSONObjectWithFields):
    """Typed variant supporting automatic type-based deserialization"""
    
    type_field_name: str = "typ"  # JSON field containing type information
    TYPES: Dict[str, Type] = {}   # Registry of types for deserialization

Usage Examples

from josepy import field, JSONObjectWithFields
from josepy.json_util import TypedJSONObjectWithFields

# Define a custom JSON object with fields
class Person(JSONObjectWithFields):
    name: str = field('name')
    age: int = field('age', default=0)
    email: str = field('email', omitempty=True)

# Create and serialize
person = Person(name="Alice", age=30, email="alice@example.com")
person_json = person.json_dumps()
print(f"Person JSON: {person_json}")

# Deserialize
loaded_person = Person.json_loads(person_json)
print(f"Loaded: {loaded_person.name}, age {loaded_person.age}")

# Typed objects with automatic type detection
class Message(TypedJSONObjectWithFields):
    type_field_name = "type"
    
    content: str = field('content')

class ErrorMessage(Message):
    error_code: int = field('error_code')

class InfoMessage(Message):
    info_level: str = field('info_level')

# Register types
Message.TYPES['error'] = ErrorMessage
Message.TYPES['info'] = InfoMessage

# Automatic deserialization based on type field
error_json = '{"type": "error", "content": "Failed", "error_code": 500}'
msg = Message.from_json(json.loads(error_json))
print(f"Message type: {type(msg).__name__}")  # ErrorMessage

Utility Classes

Additional utility classes for key handling and immutable data structures.

class ComparableKey:
    """Comparable wrapper for cryptography keys"""
    
    def __init__(self, wrapped): ...
    def public_key(self) -> 'ComparableKey': ...
    def __eq__(self, other) -> bool: ...
    def __hash__(self) -> int: ...

class ComparableRSAKey(ComparableKey):
    """Comparable wrapper for RSA keys"""

class ComparableECKey(ComparableKey):
    """Comparable wrapper for EC keys"""

class ImmutableMap:
    """Immutable key-to-value mapping with attribute access"""
    
    def __init__(self, **kwargs): ...
    def update(self, **kwargs) -> 'ImmutableMap': ...
    def __getitem__(self, key: str) -> Any: ...
    def __iter__(self): ...
    def __len__(self) -> int: ...
    def __hash__(self) -> int: ...

Usage Examples

from josepy import ComparableRSAKey, ImmutableMap
from cryptography.hazmat.primitives.asymmetric import rsa
from cryptography.hazmat.backends import default_backend

# Comparable keys enable equality comparison
key1 = rsa.generate_private_key(65537, 2048, default_backend())
key2 = rsa.generate_private_key(65537, 2048, default_backend())

comp_key1 = ComparableRSAKey(key1)
comp_key2 = ComparableRSAKey(key2)
comp_key1_copy = ComparableRSAKey(key1)

print(f"Keys equal: {comp_key1 == comp_key1_copy}")  # True
print(f"Different keys: {comp_key1 == comp_key2}")   # False

# Keys can be used in sets and dictionaries
key_set = {comp_key1, comp_key2, comp_key1_copy}
print(f"Unique keys: {len(key_set)}")  # 2

# Immutable maps
config = ImmutableMap(host="localhost", port=8080, ssl=True)
print(f"Host: {config.host}, Port: {config.port}")

# Update creates new instance
new_config = config.update(port=9090, debug=True)
print(f"New port: {new_config.port}, Debug: {new_config.debug}")
print(f"Original port: {config.port}")  # Unchanged

# Can be used as dict keys (hashable)
configs = {config: "production", new_config: "development"}

Error Handling

All utilities may raise specific exceptions for validation and encoding failures:

from josepy.errors import DeserializationError

try:
    # Invalid base64
    decoded = b64decode("invalid base64!")
except ValueError as e:
    print(f"Base64 decode error: {e}")

try:
    # Size validation failure
    data = decode_b64jose("dGVzdA", size=10)  # "test" is only 4 bytes
except DeserializationError as e:
    print(f"Size validation error: {e}")

try:
    # Invalid certificate data
    cert = decode_cert("not a certificate")
except DeserializationError as e:
    print(f"Certificate decode error: {e}")

Install with Tessl CLI

npx tessl i tessl/pypi-josepy

docs

errors.md

index.md

interfaces.md

jwa.md

jwk.md

jws.md

utilities.md

tile.json