tiny_ca.utils.serial_generator module¶
serial_generator.py
X.509 certificate serial number generation and parsing utilities.
Two implementations are provided:
SerialGenerator— stateful generator that maintains an internalmapping between serial numbers and their source identifiers (int or str). Suitable when an authoritative in-process registry is acceptable.
SerialWithEncoding— stateless generator that encodes a short nameprefix and a random UUID fragment into a single integer. No shared state; safe for concurrent use.
SOLID compliance¶
- SRPEach class has a single reason to change.
SerialGenerator→ manages serial ↔ id mapping.SerialWithEncoding→ encodes/decodes name + random bits._PrefixRegistry→ owns the CertType ↔ hex-prefix mapping.- OCPNew
CertTypevariants require only a new entry in_PrefixRegistry; no existing logic changes.
- LSPBoth generators expose compatible
generate/parsesignatures, so they can be swapped behind an
ISerialGeneratorProtocol.
ISP : ISerialGenerator declares only the two methods callers actually need.
DIP : CertificateFactory (and other consumers) depend on ISerialGenerator,
not on concrete classes.
- class tiny_ca.utils.serial_generator.ISerialGenerator(*args, **kwargs)[source]¶
Bases:
ProtocolMinimal contract for serial-number generators.
Any class that exposes
generateandparsewith compatible signatures satisfies this Protocol and can be injected wherever certificate serial numbers are needed.- __init__(*args, **kwargs)¶
- class tiny_ca.utils.serial_generator.SerialGenerator[source]¶
Bases:
objectStateful serial-number generator with a bidirectional id ↔ serial registry.
Serial number layout (64-bit integer)
[ 16-bit prefix ][ 48-bit data ]
prefix — 2-byte ASCII code derived from
CertType(see_PrefixRegistry).- data — for
intinputs: the value itself (truncated to 48 bits); for
strinputs: an auto-incrementing counter per type.
- data — for
The mapping between serial numbers and their original identifiers is kept in instance-level dictionaries, making this class unsuitable for concurrent multi-process use without external synchronisation.
- _last_serial¶
Per-type auto-increment counter for string-based identifiers.
- generate(id_value, serial_type)[source]¶
Generate a serial number for id_value and register the mapping.
For integer inputs the value is embedded directly into the data field (truncated to 48 bits). For string inputs a per-type auto-increment counter is used and the original string is stored in
_id_mapfor later retrieval byparse().- Parameters:
- Returns:
64-bit serial number:
(prefix << 48) | data.- Return type:
- Raises:
KeyError – If serial_type has no registered prefix.
- get_serial_by_name(name, serial_type)[source]¶
Look up the serial number previously assigned to a string identifier.
- Parameters:
name (str) – Original string identifier passed to
generate().serial_type (CertType) – Certificate type under which the name was registered.
- Returns:
Registered serial number, or
Noneif not found.- Return type:
int | None
- class tiny_ca.utils.serial_generator.SerialWithEncoding[source]¶
Bases:
objectStateless serial-number generator that encodes a short name prefix and a UUID-derived random fragment into a single integer.
Serial number layout
[ 16-bit prefix ][ 80-bit encoded name ][ 64-bit random ]
Total width: 160 bits (well within Python’s arbitrary-precision int; X.509 allows up to 20 bytes / 160 bits per RFC 5280 §4.1.2.2).
prefix — 2-byte ASCII code from
_PrefixRegistry.- encoded name — up to 4 ASCII characters packed into 32 bits
(little-endian byte order, zero-padded).
- random — lower 64 bits of a fresh
uuid.uuid4()ensuring global uniqueness without shared state.
- random — lower 64 bits of a fresh
Because no mutable state is kept, this class is safe to use from multiple threads or processes simultaneously.
Class Attributes¶
- RANDOM_BITSint
Number of bits reserved for the random (UUID) portion. Default:
64.- NAME_BITSint
Number of bits reserved for the encoded name portion. Default:
32(4 bytes × 8 bits).- MAX_NAME_LENGTHint
Maximum number of ASCII characters that can be encoded. Default:
4.
- classmethod generate(name, serial_type)[source]¶
Generate a globally unique serial number for name and serial_type.
Only the first
MAX_NAME_LENGTHcharacters of name are encoded; uniqueness is guaranteed by the UUID random segment, not by the name.- Parameters:
- Returns:
Non-negative integer serial suitable for X.509 certificates.
- Return type:
- Raises:
KeyError – If serial_type has no registered prefix.
Examples
>>> serial = SerialWithEncoding.generate("nginx", CertType.SERVICE) >>> cert_type, name = SerialWithEncoding.parse(serial) >>> assert cert_type == CertType.SERVICE >>> assert name == "ngin" # only first 4 chars are stored
- classmethod parse(serial)[source]¶
Decode a serial number produced by
generate().- Parameters:
serial (int) – Integer serial number to decode.
- Returns:
(cert_type, name_prefix)where name_prefix is the up-to-4-char string recovered from the encoded-name segment. cert_type isNoneif the prefix is unrecognised.- Return type:
Examples
>>> serial = SerialWithEncoding.generate("ca-root", CertType.CA) >>> cert_type, name = SerialWithEncoding.parse(serial) >>> assert cert_type == CertType.CA >>> assert name == "ca-r"