Storage Backends

Base

base_storage.py

Abstract base class that defines the storage contract for certificate artefacts.

Any class that inherits from BaseStorage and implements both abstract methods can be used wherever the application expects a storage backend, without requiring any changes to the calling code (LSP / DIP).

SOLID notes

SRPBaseStorage declares what the storage layer must do; concrete

subclasses decide how (local filesystem, S3, database BLOB, etc.).

OCPNew backends are added by subclassing; existing implementations are

not modified.

LSPSubclasses must honour the documented return contracts (e.g. returning

True for idempotent deletes rather than raising) so callers can substitute them freely.

ISPThe interface is intentionally narrow — exactly the two operations that

the application layer requires.

DIPCertLifecycleManager depends on BaseStorage, never on

LocalStorage or any other concrete class.

class tiny_ca.storage.base_storage.BaseStorage[source]

Bases: ABC

Abstract contract for certificate artefact storage backends.

Subclasses persist cryptographic objects (certificates, keys, CSRs, CRLs) to some durable medium and provide a way to remove an entire issuance folder in a single atomic call.

The two operations map directly to the two storage events in the certificate lifecycle:

  • Issuancesave_certificate is called once per artefact (.pem, .key, .csr) with the same uuid_str so all three files end up in the same directory.

  • Revocation / overwritedelete_certificate_folder removes the entire UUID directory in one call, avoiding orphaned files.

abstractmethod delete_certificate_folder(uuid_str, cert_path=None)[source]

Remove the directory that contains all artefacts for a given UUID.

Implementations must be idempotent: if the target directory does not exist, return True (no action was needed) rather than raising an error. Only genuine unexpected failures (permission errors, I/O errors) should return False.

Parameters:
  • uuid_str (str) – UUID string that identifies the sub-directory to delete. This is the value returned as the second element by save_certificate.

  • cert_path (str | Path | None) – Optional sub-path prepended before uuid_str, matching the cert_path used when the artefacts were saved. None means uuid_str is directly under the backend’s root folder.

Returns:

True — the directory was deleted, or did not exist (idempotent). False — an unexpected error occurred during deletion; the

implementation must log the exception.

Return type:

bool

abstractmethod save_certificate(cert, file_name, cert_path=None, uuid_str=None, encoding=None, private_format=None, public_format=None, encryption_algorithm=None, is_add_uuid=True, is_overwrite=False)[source]

Serialise cert and persist it to the storage backend.

The implementation must: 1. Determine the correct serialisation format and file extension from

the type of cert (certificate → .pem, private key → .key, CSR → .csr, public key → .pub, CRL → .pem).

  1. Assemble the output path from cert_path, the UUID directory (when is_add_uuid is True), and file_name + extension.

  2. Auto-generate a UUID when uuid_str is None and is_add_uuid is True; reuse uuid_str when provided (so that multiple calls for the same issuance all land in the same folder).

  3. Honour is_overwrite: raise FileAlreadyExists when False and the target path already exists; silently replace when True.

  4. Return the absolute path to the saved file and the UUID used.

Parameters:
  • cert (CryptoObject) – The cryptographic object to serialise and save.

  • file_name (str) – Base filename without extension (e.g. "ca", "my-service").

  • cert_path (str | Path | None) – Optional sub-directory appended after the backend’s root folder. None saves directly under the root.

  • uuid_str (str | None) – Explicit UUID for the issuance sub-directory. Pass the UUID returned by a previous call to group multiple artefacts in the same folder. None triggers auto-generation. Ignored when is_add_uuid is False.

  • encoding (serialization.Encoding | None) – Serialisation encoding override. None falls back to the implementation’s default (typically PEM).

  • private_format (serialization.PrivateFormat | None) – Format for private-key serialisation (e.g. TraditionalOpenSSL, PKCS8). None uses the implementation default.

  • public_format (serialization.PublicFormat | None) – Format for public-key serialisation (e.g. SubjectPublicKeyInfo). None uses the implementation default.

  • encryption_algorithm (serialization.KeySerializationEncryption | None) – Encryption wrapper for private keys (e.g. NoEncryption, BestAvailableEncryption). None uses the implementation default.

  • is_add_uuid (bool) – When True (default), a UUID subdirectory is inserted into the path. Set to False for singleton files such as CRLs that are regenerated in-place (crl.pem).

  • is_overwrite (bool) – When True, silently replace an existing file at the computed path. When False (default), raise FileAlreadyExists.

Returns:

(absolute_path_to_saved_file, uuid_str_used). The second element is the UUID that was used (auto-generated or the value of uuid_str), or None when is_add_uuid is False.

Return type:

tuple[Path, str | None]

Raises:
  • FileAlreadyExists – If the target file already exists and is_overwrite is False.

  • TypeError – If cert is not a recognised cryptographic type.

LocalStorage

local_storage.py

Local filesystem implementation of BaseStorage.

Module-level contents

_CertSerializer — stateless helper that converts a cryptographic object

to raw bytes and determines the correct file extension. Private to this module; not part of the public API.

LocalStorage — concrete BaseStorage that writes artefacts to

a configurable directory tree on the local filesystem.

SOLID notes

SRP_CertSerializer is responsible for serialisation only.

LocalStorage is responsible for path resolution and file I/O only. Neither class performs cryptographic operations or database access.

OCPNew crypto object types are handled by adding a branch in

_CertSerializer.serialise without modifying LocalStorage.

LSPLocalStorage fully honours the BaseStorage contract, including

the idempotent-delete and FileAlreadyExists raise semantics.

DIPConsumers depend on BaseStorage; LocalStorage is injected

at construction time.

class tiny_ca.storage.local_storage.LocalStorage(base_folder='./certs', base_encoding=Encoding.PEM, base_private_format=PrivateFormat.TraditionalOpenSSL, base_public_format=PublicFormat.SubjectPublicKeyInfo, base_encryption_algorithm=<cryptography.hazmat.primitives._serialization.NoEncryption object>, logger=None)[source]

Bases: BaseStorage

Local filesystem storage backend for certificate artefacts.

Writes serialised cryptographic objects to a configurable directory tree. Each issuance group (certificate + key + CSR) is placed in a dedicated UUID subdirectory so that all artefacts for a given certificate can be found and deleted together.

Directory layout

<base_folder>/
└── [cert_path/]
    └── [<uuid>/]
        ├── <file_name>.pem   # x509.Certificate or CRL
        ├── <file_name>.key   # RSA private key
        ├── <file_name>.csr   # certificate signing request
        └── <file_name>.pub   # RSA public key (if applicable)
type base_folder:

str | Path

param base_folder:

Root directory under which all certificates are stored. The directory is created on first write if it does not exist. Default: "./certs".

type base_folder:

str | Path

type base_encoding:

Encoding

param base_encoding:

Default encoding for all serialised objects. Default: Encoding.PEM.

type base_encoding:

serialization.Encoding

type base_private_format:

PrivateFormat

param base_private_format:

Default format for RSA private-key files. Default: PrivateFormat.TraditionalOpenSSL (PKCS#1, OpenSSL-compatible).

type base_private_format:

serialization.PrivateFormat

type base_public_format:

PublicFormat

param base_public_format:

Default format for RSA public-key files. Default: PublicFormat.SubjectPublicKeyInfo (PKCS#8 / X.509 SubjectPublicKeyInfo).

type base_public_format:

serialization.PublicFormat

type base_encryption_algorithm:

KeySerializationEncryption

param base_encryption_algorithm:

Default encryption applied to private-key files. Default: NoEncryption() — keys are stored in plaintext.

type base_encryption_algorithm:

serialization.KeySerializationEncryption

type logger:

Logger | None

param logger:

Logger for diagnostic messages. Falls back to DEFAULT_LOGGER.

type logger:

Logger | None

__init__(base_folder='./certs', base_encoding=Encoding.PEM, base_private_format=PrivateFormat.TraditionalOpenSSL, base_public_format=PublicFormat.SubjectPublicKeyInfo, base_encryption_algorithm=<cryptography.hazmat.primitives._serialization.NoEncryption object>, logger=None)[source]
Parameters:
Return type:

None

delete_certificate_folder(uuid_str, cert_path=None)[source]

Recursively remove the directory identified by uuid_str.

The target path is resolved as:

<base_folder> / [cert_path/] / <uuid_str>

The operation is idempotent: if the directory does not exist a UserWarning is emitted and True is returned (no action needed). If the path exists but is a regular file rather than a directory, a UserWarning is emitted and True is returned (not our directory). Only a genuine OSError during shutil.rmtree causes False.

Parameters:
  • uuid_str (str) – UUID sub-directory name to remove.

  • cert_path (str | Path | None) – Optional sub-path under base_folder containing uuid_str.

Returns:

True — directory removed, or path was already absent. FalseOSError occurred; check logs for details.

Return type:

bool

Warns:

UserWarning – If the target path does not exist or is not a directory.

save_certificate(cert, file_name, cert_path=None, uuid_str=None, encoding=None, private_format=None, public_format=None, encryption_algorithm=None, is_add_uuid=True, is_overwrite=False)[source]

Serialise cert and write the result to the local filesystem.

Assembles the output path as:

<base_folder> / [cert_path/] / [<uuid>/] / <file_name><ext>

Where ext is determined automatically from the type of cert.

Parameters:
  • cert (CryptoObject) – Cryptographic object to serialise and persist.

  • file_name (str) – Base filename without extension (e.g. "ca", "nginx").

  • cert_path (str | Path | None) – Optional sub-directory appended after base_folder.

  • uuid_str (str | None) – Reuse an existing UUID directory by passing the value returned by a previous save_certificate call. None auto-generates a new UUID. Ignored when is_add_uuid is False.

  • encoding (serialization.Encoding | None) – Encoding override. None uses base_encoding.

  • private_format (serialization.PrivateFormat | None) – Private-key format override. None uses base_private_format.

  • public_format (serialization.PublicFormat | None) – Public-key format override. None uses base_public_format.

  • encryption_algorithm (serialization.KeySerializationEncryption | None) – Private-key encryption override. None uses base_encryption_algorithm.

  • is_add_uuid (bool) – When True (default), a UUID subdirectory is inserted. Set to False for singleton files such as CRL that are regenerated in-place.

  • is_overwrite (bool) – When True, silently replace an existing file. When False (default), raise FileAlreadyExists.

Returns:

(absolute_path_to_written_file, uuid_used). uuid_used is None when is_add_uuid is False.

Return type:

tuple[Path, str | None]

Raises:
  • FileAlreadyExists – If the computed target path already exists and is_overwrite is False.

  • TypeError – If cert is not a supported cryptographic type.

Parameters:
  • base_folder (str | Path)

  • base_encoding (serialization.Encoding)

  • base_private_format (serialization.PrivateFormat)

  • base_public_format (serialization.PublicFormat)

  • base_encryption_algorithm (serialization.KeySerializationEncryption)

  • logger (Logger | None)

AsyncLocalStorage

class tiny_ca.storage.async_local_storage.AsyncLocalStorage(base_folder='./certs', base_encoding=Encoding.PEM, base_private_format=PrivateFormat.TraditionalOpenSSL, base_public_format=PublicFormat.SubjectPublicKeyInfo, base_encryption_algorithm=<cryptography.hazmat.primitives._serialization.NoEncryption object>, logger=None)[source]

Bases: LocalStorage

Parameters:
  • base_folder (str | Path)

  • base_encoding (serialization.Encoding)

  • base_private_format (serialization.PrivateFormat)

  • base_public_format (serialization.PublicFormat)

  • base_encryption_algorithm (serialization.KeySerializationEncryption)

  • logger (Logger | None)

async delete_certificate_folder(uuid_str, cert_path=None)[source]

Recursively remove the directory identified by uuid_str.

The target path is resolved as:

<base_folder> / [cert_path/] / <uuid_str>

The operation is idempotent: if the directory does not exist a UserWarning is emitted and True is returned (no action needed). If the path exists but is a regular file rather than a directory, a UserWarning is emitted and True is returned (not our directory). Only a genuine OSError during shutil.rmtree causes False.

Parameters:
  • uuid_str (str) – UUID sub-directory name to remove.

  • cert_path (str | Path | None) – Optional sub-path under base_folder containing uuid_str.

Returns:

True — directory removed, or path was already absent. FalseOSError occurred; check logs for details.

Return type:

bool

Warns:

UserWarning – If the target path does not exist or is not a directory.

async save_certificate(cert, file_name, cert_path=None, uuid_str=None, encoding=None, private_format=None, public_format=None, encryption_algorithm=None, is_add_uuid=True, is_overwrite=False)[source]

Serialise cert and write the result to the local filesystem.

Assembles the output path as:

<base_folder> / [cert_path/] / [<uuid>/] / <file_name><ext>

Where ext is determined automatically from the type of cert.

Parameters:
  • cert (CryptoObject) – Cryptographic object to serialise and persist.

  • file_name (str) – Base filename without extension (e.g. "ca", "nginx").

  • cert_path (str | Path | None) – Optional sub-directory appended after base_folder.

  • uuid_str (str | None) – Reuse an existing UUID directory by passing the value returned by a previous save_certificate call. None auto-generates a new UUID. Ignored when is_add_uuid is False.

  • encoding (serialization.Encoding | None) – Encoding override. None uses base_encoding.

  • private_format (serialization.PrivateFormat | None) – Private-key format override. None uses base_private_format.

  • public_format (serialization.PublicFormat | None) – Public-key format override. None uses base_public_format.

  • encryption_algorithm (serialization.KeySerializationEncryption | None) – Private-key encryption override. None uses base_encryption_algorithm.

  • is_add_uuid (bool) – When True (default), a UUID subdirectory is inserted. Set to False for singleton files such as CRL that are regenerated in-place.

  • is_overwrite (bool) – When True, silently replace an existing file. When False (default), raise FileAlreadyExists.

Returns:

(absolute_path_to_written_file, uuid_used). uuid_used is None when is_add_uuid is False.

Return type:

tuple[Path, str | None]

Raises:
  • FileAlreadyExists – If the computed target path already exists and is_overwrite is False.

  • TypeError – If cert is not a supported cryptographic type.

Constants

storage/const.py

Shared type aliases for the storage layer.

Exports CryptoObject, a union type alias covering every cryptographic object that BaseStorage.save_certificate is capable of serialising and persisting. Declaring it once here prevents duplication across base_storage.py, local_storage.py, and any future storage backends.

tiny_ca.storage.const.CryptoObject = cryptography.hazmat.bindings._rust.x509.Certificate | cryptography.hazmat.bindings._rust.x509.CertificateRevocationList | cryptography.hazmat.bindings._rust.x509.CertificateSigningRequest | cryptography.hazmat.primitives.asymmetric.rsa.RSAPrivateKey | cryptography.hazmat.primitives.asymmetric.rsa.RSAPublicKey

Union of all cryptographic object types accepted by the storage layer.

Type

Extension

x509.Certificate

.pem

x509.CertificateRevocationList

.pem

CertificateSigningRequest (Rust)

.csr

rsa.RSAPrivateKey

.key

rsa.RSAPublicKey

.pub

The internal Rust-backed CertificateSigningRequest type is included because the cryptography library exposes CSR objects through its Rust bindings rather than a pure-Python class.