API reference
=============
Algorithm constants
-------------------
.. class:: Algorithm
Static class containing W3C XML Security algorithm URI strings. Use these
instead of hardcoding URI strings in your code.
.. code-block:: python
from pybergshamra import Algorithm
print(Algorithm.RSA_SHA256)
# "http://www.w3.org/2001/04/xmldsig-more#rsa-sha256"
**Canonicalization**
.. attribute:: C14N
:type: str
Inclusive C14N (``http://www.w3.org/TR/2001/REC-xml-c14n-20010315``)
.. attribute:: C14N_WITH_COMMENTS
:type: str
Inclusive C14N with comments
.. attribute:: C14N11
:type: str
Inclusive C14N 1.1 (``http://www.w3.org/2006/12/xml-c14n11``)
.. attribute:: C14N11_WITH_COMMENTS
:type: str
Inclusive C14N 1.1 with comments
.. attribute:: EXC_C14N
:type: str
Exclusive C14N (``http://www.w3.org/2001/10/xml-exc-c14n#``)
.. attribute:: EXC_C14N_WITH_COMMENTS
:type: str
Exclusive C14N with comments
**Digest**
.. attribute:: SHA1
:type: str
SHA-1 digest
.. attribute:: SHA224
:type: str
SHA-224 digest
.. attribute:: SHA256
:type: str
SHA-256 digest
.. attribute:: SHA384
:type: str
SHA-384 digest
.. attribute:: SHA512
:type: str
SHA-512 digest
.. attribute:: SHA3_224
:type: str
SHA3-224 digest
.. attribute:: SHA3_256
:type: str
SHA3-256 digest
.. attribute:: SHA3_384
:type: str
SHA3-384 digest
.. attribute:: SHA3_512
:type: str
SHA3-512 digest
.. attribute:: MD5
:type: str
MD5 digest (legacy, not recommended)
.. attribute:: RIPEMD160
:type: str
RIPEMD-160 digest (legacy)
**RSA Signature**
.. attribute:: RSA_SHA1
:type: str
RSA with SHA-1
.. attribute:: RSA_SHA224
:type: str
RSA with SHA-224
.. attribute:: RSA_SHA256
:type: str
RSA with SHA-256
.. attribute:: RSA_SHA384
:type: str
RSA with SHA-384
.. attribute:: RSA_SHA512
:type: str
RSA with SHA-512
.. attribute:: RSA_MD5
:type: str
RSA with MD5 (legacy, not recommended)
.. attribute:: RSA_RIPEMD160
:type: str
RSA with RIPEMD-160 (legacy)
**RSA-PSS Signature**
.. attribute:: RSA_PSS_SHA1
:type: str
RSA-PSS with SHA-1
.. attribute:: RSA_PSS_SHA224
:type: str
RSA-PSS with SHA-224
.. attribute:: RSA_PSS_SHA256
:type: str
RSA-PSS with SHA-256
.. attribute:: RSA_PSS_SHA384
:type: str
RSA-PSS with SHA-384
.. attribute:: RSA_PSS_SHA512
:type: str
RSA-PSS with SHA-512
.. attribute:: RSA_PSS_SHA3_224
:type: str
RSA-PSS with SHA3-224
.. attribute:: RSA_PSS_SHA3_256
:type: str
RSA-PSS with SHA3-256
.. attribute:: RSA_PSS_SHA3_384
:type: str
RSA-PSS with SHA3-384
.. attribute:: RSA_PSS_SHA3_512
:type: str
RSA-PSS with SHA3-512
**DSA Signature**
.. attribute:: DSA_SHA1
:type: str
DSA with SHA-1
.. attribute:: DSA_SHA256
:type: str
DSA with SHA-256
**ECDSA Signature**
.. attribute:: ECDSA_SHA1
:type: str
ECDSA with SHA-1
.. attribute:: ECDSA_SHA224
:type: str
ECDSA with SHA-224
.. attribute:: ECDSA_SHA256
:type: str
ECDSA with SHA-256
.. attribute:: ECDSA_SHA384
:type: str
ECDSA with SHA-384
.. attribute:: ECDSA_SHA512
:type: str
ECDSA with SHA-512
.. attribute:: ECDSA_SHA3_224
:type: str
ECDSA with SHA3-224
.. attribute:: ECDSA_SHA3_256
:type: str
ECDSA with SHA3-256
.. attribute:: ECDSA_SHA3_384
:type: str
ECDSA with SHA3-384
.. attribute:: ECDSA_SHA3_512
:type: str
ECDSA with SHA3-512
.. attribute:: ECDSA_RIPEMD160
:type: str
ECDSA with RIPEMD-160 (legacy)
**EdDSA Signature**
.. attribute:: EDDSA_ED25519
:type: str
Ed25519 signature
**HMAC Signature**
.. attribute:: HMAC_SHA1
:type: str
HMAC with SHA-1
.. attribute:: HMAC_SHA224
:type: str
HMAC with SHA-224
.. attribute:: HMAC_SHA256
:type: str
HMAC with SHA-256
.. attribute:: HMAC_SHA384
:type: str
HMAC with SHA-384
.. attribute:: HMAC_SHA512
:type: str
HMAC with SHA-512
.. attribute:: HMAC_MD5
:type: str
HMAC with MD5 (legacy, not recommended)
.. attribute:: HMAC_RIPEMD160
:type: str
HMAC with RIPEMD-160 (legacy)
**ML-DSA Post-Quantum**
.. attribute:: ML_DSA_44
:type: str
ML-DSA-44 (FIPS 204)
.. attribute:: ML_DSA_65
:type: str
ML-DSA-65 (FIPS 204)
.. attribute:: ML_DSA_87
:type: str
ML-DSA-87 (FIPS 204)
**SLH-DSA Post-Quantum**
.. attribute:: SLH_DSA_SHA2_128F
:type: str
SLH-DSA-SHA2-128f (FIPS 205)
.. attribute:: SLH_DSA_SHA2_128S
:type: str
SLH-DSA-SHA2-128s (FIPS 205)
.. attribute:: SLH_DSA_SHA2_192F
:type: str
SLH-DSA-SHA2-192f (FIPS 205)
.. attribute:: SLH_DSA_SHA2_192S
:type: str
SLH-DSA-SHA2-192s (FIPS 205)
.. attribute:: SLH_DSA_SHA2_256F
:type: str
SLH-DSA-SHA2-256f (FIPS 205)
.. attribute:: SLH_DSA_SHA2_256S
:type: str
SLH-DSA-SHA2-256s (FIPS 205)
**Block Cipher**
.. attribute:: AES128_CBC
:type: str
AES-128 in CBC mode
.. attribute:: AES192_CBC
:type: str
AES-192 in CBC mode
.. attribute:: AES256_CBC
:type: str
AES-256 in CBC mode
.. attribute:: AES128_GCM
:type: str
AES-128 in GCM mode
.. attribute:: AES192_GCM
:type: str
AES-192 in GCM mode
.. attribute:: AES256_GCM
:type: str
AES-256 in GCM mode
.. attribute:: TRIPLEDES_CBC
:type: str
Triple DES in CBC mode (legacy)
**Key Wrap**
.. attribute:: KW_AES128
:type: str
AES-128 key wrap
.. attribute:: KW_AES192
:type: str
AES-192 key wrap
.. attribute:: KW_AES256
:type: str
AES-256 key wrap
.. attribute:: KW_TRIPLEDES
:type: str
Triple DES key wrap (legacy)
**Key Transport**
.. attribute:: RSA_PKCS1
:type: str
RSA PKCS#1 v1.5 key transport (legacy)
.. attribute:: RSA_OAEP
:type: str
RSA-OAEP key transport
.. attribute:: RSA_OAEP_ENC11
:type: str
RSA-OAEP (XML Encryption 1.1)
**Mask Generation Function (MGF)**
.. attribute:: MGF1_SHA1
:type: str
MGF1 with SHA-1
.. attribute:: MGF1_SHA224
:type: str
MGF1 with SHA-224
.. attribute:: MGF1_SHA256
:type: str
MGF1 with SHA-256
.. attribute:: MGF1_SHA384
:type: str
MGF1 with SHA-384
.. attribute:: MGF1_SHA512
:type: str
MGF1 with SHA-512
**Key Agreement**
.. attribute:: DH_ES
:type: str
Diffie-Hellman ephemeral-static
.. attribute:: ECDH_ES
:type: str
ECDH ephemeral-static
.. attribute:: X25519
:type: str
X25519 key agreement
**Key Derivation**
.. attribute:: PBKDF2
:type: str
PBKDF2 key derivation
.. attribute:: CONCAT_KDF
:type: str
ConcatKDF key derivation
.. attribute:: HKDF
:type: str
HKDF key derivation
**Transform**
.. attribute:: BASE64
:type: str
Base64 transform
.. attribute:: ENVELOPED_SIGNATURE
:type: str
Enveloped signature transform
.. attribute:: XPATH
:type: str
XPath transform
.. attribute:: XPATH2
:type: str
XPath 2.0 filter transform
.. attribute:: XSLT
:type: str
XSLT transform
.. attribute:: XPOINTER
:type: str
XPointer transform
.. attribute:: RELATIONSHIP
:type: str
Relationship transform
**KeyValue Type**
.. attribute:: RSA_KEY_VALUE
:type: str
RSA KeyValue
.. attribute:: DSA_KEY_VALUE
:type: str
DSA KeyValue
.. attribute:: EC_KEY_VALUE
:type: str
EC KeyValue
.. attribute:: DH_KEY_VALUE
:type: str
DH KeyValue
.. attribute:: DER_ENCODED_KEY_VALUE
:type: str
DER-encoded KeyValue
**X509**
.. attribute:: X509_DATA
:type: str
X509Data key data
.. attribute:: RAW_X509_CERT
:type: str
Raw X.509 certificate
**Encrypted/Derived Key**
.. attribute:: ENCRYPTED_KEY
:type: str
EncryptedKey
.. attribute:: DERIVED_KEY
:type: str
DerivedKey
Key management
--------------
KeyUsage
^^^^^^^^
.. class:: KeyUsage
Key usage mode enumeration. Pass these values to :meth:`Key.usage` or
:meth:`KeysManager.find_by_usage`.
.. attribute:: Sign
Key is used for signing.
.. attribute:: Verify
Key is used for signature verification.
.. attribute:: Encrypt
Key is used for encryption.
.. attribute:: Decrypt
Key is used for decryption.
.. attribute:: Any
Key can be used for any purpose (default).
.. code-block:: python
import pybergshamra
from pybergshamra import KeyUsage
key = pybergshamra.load_rsa_private_pem(pem_data)
key.usage = KeyUsage.Sign
manager = pybergshamra.KeysManager()
manager.add_key(key)
found = manager.find_by_usage(KeyUsage.Sign)
Key
^^^
.. class:: Key
A cryptographic key (RSA, EC, HMAC, AES, Ed25519, X25519, PQ, etc.).
Keys are created through the key loader functions, not constructed directly.
.. attribute:: name
:type: str | None
The key name, or ``None``. Readable and writable.
.. code-block:: python
key = pybergshamra.load_rsa_private_pem(pem_data)
key.name = "my-signing-key"
print(key.name) # "my-signing-key"
.. attribute:: usage
:type: KeyUsage
The key usage mode. Readable and writable.
.. attribute:: algorithm_name
:type: str
The algorithm name (read-only). Examples: ``"RSA"``, ``"EC-P256"``,
``"HMAC"``, ``"AES"``, ``"3DES"``, ``"Ed25519"``, ``"X25519"``.
.. code-block:: python
key = pybergshamra.load_rsa_private_pem(pem_data)
print(key.algorithm_name) # "RSA"
.. attribute:: has_private_key
:type: bool
Whether this key contains private key material (read-only).
.. attribute:: x509_chain
:type: list[bytes]
The DER-encoded X.509 certificate chain, if present (read-only).
.. method:: to_spki_der() -> bytes | None
Return the SPKI DER encoding of the public key, or ``None`` if not
available.
.. method:: symmetric_key_bytes() -> bytes | None
Return the raw symmetric key bytes (HMAC/AES/DES3), or ``None``.
.. method:: ec_public_key_bytes() -> bytes | None
Return the uncompressed EC public key bytes, or ``None``.
.. method:: x25519_public_key_bytes() -> bytes | None
Return the X25519 public key bytes (32 bytes), or ``None``.
.. method:: x25519_private_key_bytes() -> bytes | None
Return the X25519 private key bytes (32 bytes), or ``None``.
.. method:: to_key_value_xml(prefix: str = "ds") -> str | None
Return the KeyValue XML fragment, or ``None``. The returned XML uses
the given namespace prefix.
.. code-block:: python
key = pybergshamra.load_rsa_private_pem(pem_data)
xml_fragment = key.to_key_value_xml()
# Returns e.g. "..."
KeysManager
^^^^^^^^^^^
.. class:: KeysManager()
Key store for managing cryptographic keys and certificates.
.. code-block:: python
import pybergshamra
manager = pybergshamra.KeysManager()
key = pybergshamra.load_rsa_private_pem(pem_data)
manager.add_key(key)
print(len(manager)) # 1
print(bool(manager)) # True
.. method:: add_key(key: Key) -> None
Add a key to the manager.
.. method:: insert_key_first(key: Key) -> None
Insert a key at the front (becomes the first key).
.. method:: first_key() -> Key | None
Return the first key, or ``None`` if the manager is empty.
.. method:: keys() -> list[Key]
Return all keys as a list.
.. method:: find_by_name(name: str) -> Key | None
Find a key by name.
.. code-block:: python
key = pybergshamra.load_hmac_key(b"secret")
key.name = "my-hmac"
manager.add_key(key)
found = manager.find_by_name("my-hmac")
.. method:: find_by_usage(usage: KeyUsage) -> Key | None
Find a key by usage mode.
.. method:: find_rsa() -> Key | None
Find the first RSA key.
.. method:: find_rsa_private() -> Key | None
Find the first RSA key with private material.
.. method:: find_hmac() -> Key | None
Find the first HMAC key.
.. method:: find_aes() -> Key | None
Find the first AES key.
.. method:: find_aes_by_size(size_bytes: int) -> Key | None
Find the first AES key matching the given size in bytes (16, 24, or 32).
.. method:: find_des3() -> Key | None
Find the first 3DES key.
.. method:: find_ec_p256() -> Key | None
Find the first EC P-256 key.
.. method:: find_ec_p384() -> Key | None
Find the first EC P-384 key.
.. method:: find_ec_p521() -> Key | None
Find the first EC P-521 key.
.. method:: find_ed25519() -> Key | None
Find the first Ed25519 key.
.. method:: find_x25519() -> Key | None
Find the first X25519 key.
.. method:: find_pq() -> Key | None
Find the first post-quantum key.
.. method:: find_dh() -> Key | None
Find the first DH key.
.. method:: add_trusted_cert(der: bytes) -> None
Add a trusted DER-encoded X.509 certificate.
.. method:: add_untrusted_cert(der: bytes) -> None
Add an untrusted DER-encoded X.509 certificate.
.. method:: add_crl(der: bytes) -> None
Add a DER-encoded CRL.
.. method:: trusted_certs() -> list[bytes]
Return the trusted certificates as a list of DER bytes.
.. method:: untrusted_certs() -> list[bytes]
Return the untrusted certificates as a list of DER bytes.
.. method:: crls() -> list[bytes]
Return the CRLs as a list of DER bytes.
.. method:: has_trusted_certs() -> bool
Whether the manager has any trusted certificates.
Key loaders
-----------
File-based loaders
^^^^^^^^^^^^^^^^^^
.. function:: load_key_file(path: str) -> Key
Load a key from a file, auto-detecting the format by extension
(``.pem``, ``.der``, ``.cer``, ``.p12``, etc.).
:raises KeyLoadError: If the file cannot be read or parsed.
.. code-block:: python
key = pybergshamra.load_key_file("rsakey.pem")
.. function:: load_key_file_with_password(path: str, password: str) -> Key
Load a key from a password-protected file.
:raises KeyLoadError: If the file cannot be read, the password is wrong,
or the format is unsupported.
.. code-block:: python
key = pybergshamra.load_key_file_with_password("cakey.pem", "secret123")
.. function:: load_pkcs12(data: bytes, password: str) -> Key
Load a key from PKCS#12 (PFX) data with a password.
:param data: Raw PKCS#12 bytes.
:param password: The password protecting the PKCS#12 file.
:raises KeyLoadError: If the data is invalid or the password is wrong.
.. code-block:: python
p12_data = open("key.p12", "rb").read()
key = pybergshamra.load_pkcs12(p12_data, "secret123")
.. function:: load_keys_file(path: str) -> list[Key]
Load keys from an xmlsec ``keys.xml`` file.
:raises KeyLoadError: If the file cannot be read or parsed.
.. code-block:: python
keys = pybergshamra.load_keys_file("keys.xml")
for key in keys:
print(key.name, key.algorithm_name)
RSA loaders
^^^^^^^^^^^
.. function:: load_rsa_private_pem(pem_data: bytes) -> Key
Load an RSA private key from PEM data.
:raises KeyLoadError: If the PEM data is not a valid RSA private key.
.. function:: load_rsa_public_pem(pem_data: bytes) -> Key
Load an RSA public key from PEM data.
:raises KeyLoadError: If the PEM data is not a valid RSA public key.
EC loaders
^^^^^^^^^^
.. function:: load_ec_p256_private_pem(pem_data: bytes) -> Key
Load an EC P-256 (secp256r1) private key from PEM data.
:raises KeyLoadError: If the PEM data is not a valid EC P-256 key.
.. function:: load_ec_p384_private_pem(pem_data: bytes) -> Key
Load an EC P-384 (secp384r1) private key from PEM data.
:raises KeyLoadError: If the PEM data is not a valid EC P-384 key.
.. function:: load_ec_p521_private_pem(pem_data: bytes) -> Key
Load an EC P-521 (secp521r1) private key from PEM data.
:raises KeyLoadError: If the PEM data is not a valid EC P-521 key.
X.509 loaders
^^^^^^^^^^^^^
.. function:: load_x509_cert_pem(pem_data: bytes) -> Key
Load an X.509 certificate from PEM data. The returned key contains
the certificate's public key and the certificate chain.
:raises KeyLoadError: If the PEM data is not a valid certificate.
.. code-block:: python
cert_pem = open("cert.pem", "rb").read()
key = pybergshamra.load_x509_cert_pem(cert_pem)
print(key.algorithm_name) # e.g. "RSA"
print(len(key.x509_chain)) # 1
.. function:: load_x509_cert_der(data: bytes) -> Key
Load an X.509 certificate from DER data.
:raises KeyLoadError: If the data is not a valid DER certificate.
Symmetric key loaders
^^^^^^^^^^^^^^^^^^^^^
.. function:: load_hmac_key(data: bytes) -> Key
Create an HMAC key from raw bytes. This function never fails.
.. code-block:: python
key = pybergshamra.load_hmac_key(b"my-secret-key")
print(key.algorithm_name) # "HMAC"
.. function:: load_aes_key(data: bytes) -> Key
Create an AES key from raw bytes. The data length must be 16 (AES-128),
24 (AES-192), or 32 (AES-256) bytes.
:raises KeyLoadError: If the data length is invalid.
.. code-block:: python
import os
key = pybergshamra.load_aes_key(os.urandom(32)) # AES-256
.. function:: load_des3_key(data: bytes) -> Key
Create a 3DES key from raw bytes. The data must be 24 bytes.
:raises KeyLoadError: If the data length is not 24.
Auto-detect loaders
^^^^^^^^^^^^^^^^^^^
.. function:: load_pem_auto(pem_data: bytes, password: str | None = None) -> Key
Auto-detect the PEM type and load the key. Handles RSA, EC, Ed25519,
X.509 certificates, PKCS#8, and encrypted PEM.
:param pem_data: PEM-encoded key or certificate bytes.
:param password: Optional password for encrypted PEM files.
:raises KeyLoadError: If the PEM type cannot be determined or loaded.
.. code-block:: python
# Works with any PEM type
key = pybergshamra.load_pem_auto(open("some-key.pem", "rb").read())
# With password for encrypted PEM
key = pybergshamra.load_pem_auto(open("cakey.pem", "rb").read(), password="secret123")
SPKI loaders
^^^^^^^^^^^^
.. function:: load_spki_pem(pem_data: bytes) -> Key
Load a public key from SPKI (Subject Public Key Info) PEM data.
:raises KeyLoadError: If the PEM data is not valid SPKI.
.. function:: load_spki_der(data: bytes) -> Key
Load a public key from SPKI DER data.
:raises KeyLoadError: If the data is not valid SPKI DER.
Ed25519 loaders
^^^^^^^^^^^^^^^
.. function:: load_ed25519_private_pkcs8_der(data: bytes) -> Key
Load an Ed25519 private key from PKCS#8 DER data.
:raises KeyLoadError: If the data is not a valid Ed25519 PKCS#8 key.
.. function:: load_ed25519_public_spki_der(data: bytes) -> Key
Load an Ed25519 public key from SPKI DER data.
:raises KeyLoadError: If the data is not a valid Ed25519 SPKI key.
X25519 loaders
^^^^^^^^^^^^^^
.. function:: load_x25519_private_raw(data: bytes) -> Key
Load an X25519 private key from raw 32-byte data.
:raises KeyLoadError: If the data is not exactly 32 bytes.
.. function:: load_x25519_public_raw(data: bytes) -> Key
Load an X25519 public key from raw 32-byte data.
:raises KeyLoadError: If the data is not exactly 32 bytes.
Keys XML
^^^^^^^^
.. function:: parse_keys_xml(xml: str) -> list[Key]
Parse keys from an xmlsec ``keys.xml`` string.
:raises KeyLoadError: If the XML is not a valid keys document.
.. code-block:: python
xml = open("keys.xml").read()
keys = pybergshamra.parse_keys_xml(xml)
for k in keys:
print(k.name, k.algorithm_name)
X509 KeyInfo builders
---------------------
.. function:: build_x509_key_info(certs_b64: list[str]) -> str
Build a ```` XML fragment from base64-encoded
certificates.
:param certs_b64: List of base64-encoded certificate strings.
:returns: XML string with ``ds:`` namespace prefix.
.. code-block:: python
import base64
cert_der = open("cert.der", "rb").read()
cert_b64 = base64.b64encode(cert_der).decode()
xml = pybergshamra.build_x509_key_info([cert_b64])
# '...'
.. function:: build_x509_key_info_from_der(certs_der: list[bytes]) -> str
Build a ```` XML fragment from DER-encoded
certificates.
:param certs_der: List of DER-encoded certificate bytes.
:returns: XML string with ``ds:`` namespace prefix.
.. code-block:: python
cert_der = open("cert.der", "rb").read()
xml = pybergshamra.build_x509_key_info_from_der([cert_der])
Digital signatures
------------------
DsigContext
^^^^^^^^^^^
.. class:: DsigContext(keys_manager: KeysManager)
Context for XML Digital Signature operations. Holds configuration and a
:class:`KeysManager`. Build one, set properties, then call :func:`verify`
or :func:`sign`.
:param keys_manager: The key store to use for sign/verify operations.
.. code-block:: python
manager = pybergshamra.KeysManager()
manager.add_key(key)
ctx = pybergshamra.DsigContext(manager)
.. attribute:: debug
:type: bool
Debug mode: print pre-digest and pre-signature data to stderr.
Default: ``False``.
.. attribute:: insecure
:type: bool
Insecure mode: skip certificate validation. Default: ``False``.
.. attribute:: verify_keys
:type: bool
Whether to validate certificates for keys loaded from files.
Default: ``False``.
.. attribute:: verification_time
:type: str | None
Verification time override. Format: ``"YYYY-MM-DD+HH:MM:SS"``.
Default: ``None`` (use current time).
.. attribute:: skip_time_checks
:type: bool
Skip X.509 NotBefore/NotAfter validation. Default: ``False``.
.. attribute:: enabled_key_data_x509
:type: bool
Whether enabled key data includes X.509. Default: ``False``.
.. attribute:: trusted_keys_only
:type: bool
Only use pre-configured keys, skip inline KeyInfo extraction.
Default: ``False``.
.. attribute:: strict_verification
:type: bool
Enforce strict reference target validation (anti-XSW protection).
Default: ``False``.
.. code-block:: python
ctx = pybergshamra.DsigContext(manager)
ctx.strict_verification = True
result = pybergshamra.verify(ctx, xml)
.. attribute:: hmac_min_out_len
:type: int
Minimum HMAC output length in bits. ``0`` means use the spec default.
.. attribute:: base_dir
:type: str | None
Base directory for resolving relative external URIs.
.. method:: add_id_attr(name: str) -> None
Register an additional ID attribute name. Required for SAML and other
XML formats that use custom ID attributes (e.g. ``"ID"``).
.. code-block:: python
ctx = pybergshamra.DsigContext(manager)
ctx.add_id_attr("ID") # needed for SAML
result = pybergshamra.verify(ctx, saml_xml)
.. method:: add_url_map(url: str, file_path: str) -> None
Map a URL to a local file path for external URI resolution.
.. code-block:: python
ctx.add_url_map("http://example.com/schema.xsd", "/local/schema.xsd")
VerifyResult
^^^^^^^^^^^^
.. class:: VerifyResult
Result of signature verification. Use ``bool(result)`` to check validity.
.. attribute:: is_valid
:type: bool
Whether the signature is valid.
.. attribute:: reason
:type: str | None
The reason for invalidity, or ``None`` if valid.
.. attribute:: references
:type: list[VerifiedReference] | None
The verified references, or ``None`` if invalid.
.. attribute:: key_info
:type: VerifiedKeyInfo | None
Information about the verification key, or ``None`` if invalid.
.. attribute:: signature_node_id
:type: int | None
The node ID of the ```` element, or ``None`` if invalid.
.. code-block:: python
result = pybergshamra.verify(ctx, xml)
if result:
print("Valid!")
for ref in result.references:
print(f" Reference URI: {ref.uri}")
print(f" Key algorithm: {result.key_info.algorithm}")
else:
print(f"Invalid: {result.reason}")
VerifiedReference
^^^^^^^^^^^^^^^^^
.. class:: VerifiedReference
Metadata about a single verified ```` element.
.. attribute:: uri
:type: str
The URI attribute from the ```` element.
.. attribute:: resolved_node_id
:type: int | None
The resolved target node ID (if a same-document reference).
VerifiedKeyInfo
^^^^^^^^^^^^^^^
.. class:: VerifiedKeyInfo
Information about the key used for verification.
.. attribute:: algorithm
:type: str
Algorithm name (e.g. ``"RSA"``, ``"EC-P256"``, ``"HMAC"``).
.. attribute:: key_name
:type: str | None
Key name (if resolved by name from KeysManager).
.. attribute:: x509_chain
:type: list[bytes]
DER-encoded X.509 certificate chain (leaf first).
verify and sign
^^^^^^^^^^^^^^^
.. function:: verify(ctx: DsigContext, xml: str) -> VerifyResult
Verify a signed XML document. Returns a :class:`VerifyResult` -- use
``bool(result)`` to check validity.
:param ctx: A configured :class:`DsigContext`.
:param xml: The signed XML string.
:raises XmlError: If the XML cannot be parsed.
.. code-block:: python
result = pybergshamra.verify(ctx, xml)
if result:
print("Signature valid")
.. function:: sign(ctx: DsigContext, template_xml: str) -> str
Sign an XML template and return the signed XML string. The template must
contain a ```` skeleton with ````, ````,
etc.
:param ctx: A configured :class:`DsigContext`.
:param template_xml: The XML template string.
:raises XmlError: If the XML template cannot be parsed.
:raises CryptoError: If signing fails.
.. code-block:: python
signed_xml = pybergshamra.sign(ctx, template)
print(signed_xml)
XML encryption
--------------
EncContext
^^^^^^^^^^
.. class:: EncContext(keys_manager: KeysManager)
Context for XML Encryption operations. Holds configuration and a
:class:`KeysManager`.
:param keys_manager: The key store to use for encrypt/decrypt operations.
.. attribute:: disable_cipher_reference
:type: bool
Whether CipherReference resolution is disabled. Default: ``False``.
.. method:: add_id_attr(name: str) -> None
Register an additional ID attribute name.
encrypt, decrypt, decrypt_to_bytes
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
.. function:: encrypt(ctx: EncContext, template_xml: str, data: bytes) -> str
Encrypt data using an XML template. The template must contain an
```` element with an empty ````.
:param ctx: A configured :class:`EncContext`.
:param template_xml: The XML encryption template.
:param data: The plaintext bytes to encrypt.
:returns: XML string with encrypted content.
:raises EncryptionError: If encryption fails.
.. function:: decrypt(ctx: EncContext, xml: str) -> str
Decrypt an XML document containing ````. Returns the
decrypted XML as a string.
:param ctx: A configured :class:`EncContext`.
:param xml: The XML string containing encrypted data.
:raises EncryptionError: If decryption fails.
:raises XmlError: If the XML cannot be parsed.
.. function:: decrypt_to_bytes(ctx: EncContext, xml: str) -> bytes
Decrypt an XML document containing ````. Returns the raw
decrypted bytes (supports non-UTF-8 content).
:param ctx: A configured :class:`EncContext`.
:param xml: The XML string containing encrypted data.
:raises EncryptionError: If decryption fails.
:raises XmlError: If the XML cannot be parsed.
Canonicalization
----------------
C14nMode
^^^^^^^^
.. class:: C14nMode
XML Canonicalization mode enumeration.
.. attribute:: Inclusive
Inclusive C14N (``http://www.w3.org/TR/2001/REC-xml-c14n-20010315``)
.. attribute:: InclusiveWithComments
Inclusive C14N with comments
.. attribute:: Inclusive11
Inclusive C14N 1.1
.. attribute:: Inclusive11WithComments
Inclusive C14N 1.1 with comments
.. attribute:: Exclusive
Exclusive C14N (``http://www.w3.org/2001/10/xml-exc-c14n#``)
.. attribute:: ExclusiveWithComments
Exclusive C14N with comments
**Properties**
.. attribute:: uri
:type: str
The W3C algorithm URI for this mode.
.. attribute:: with_comments
:type: bool
Whether this mode includes comments.
.. attribute:: is_exclusive
:type: bool
Whether this mode uses exclusive canonicalization.
**Methods**
.. staticmethod:: from_uri(uri: str) -> C14nMode | None
Look up a ``C14nMode`` from its W3C algorithm URI. Returns ``None``
if the URI is not recognized.
.. code-block:: python
from pybergshamra import C14nMode
mode = C14nMode.from_uri("http://www.w3.org/2001/10/xml-exc-c14n#")
assert mode == C14nMode.Exclusive
canonicalize and canonicalize_subtree
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
.. function:: canonicalize(xml: str, mode: C14nMode, inclusive_prefixes: list[str] | None = None) -> bytes
Canonicalize an XML document.
:param xml: The XML string.
:param mode: The canonicalization mode.
:param inclusive_prefixes: Optional namespace prefixes to force
visibly-utilized in exclusive C14N.
:returns: The canonicalized XML as bytes.
:raises XmlError: If the XML cannot be parsed.
.. code-block:: python
from pybergshamra import canonicalize, C14nMode
result = canonicalize("", C14nMode.Exclusive)
.. function:: canonicalize_subtree(xml: str, element_id: str, mode: C14nMode, inclusive_prefixes: list[str] | None = None) -> bytes
Canonicalize a subtree identified by an element ID.
:param xml: The XML string.
:param element_id: The ID attribute value of the target element.
:param mode: The canonicalization mode.
:param inclusive_prefixes: Optional namespace prefixes.
:returns: The canonicalized subtree as bytes.
:raises XmlError: If the XML cannot be parsed or the element is not found.
.. code-block:: python
from pybergshamra import canonicalize_subtree, C14nMode
xml = '- hello
'
result = canonicalize_subtree(xml, "x1", C14nMode.Exclusive)
Cryptographic primitives
------------------------
.. function:: digest(algorithm_uri: str, data: bytes) -> bytes
Compute a one-shot message digest.
:param algorithm_uri: A W3C algorithm URI (e.g. ``Algorithm.SHA256``).
:param data: The data to digest.
:returns: The digest bytes.
:raises AlgorithmError: If the algorithm is unsupported.
.. code-block:: python
from pybergshamra import digest, Algorithm
h = digest(Algorithm.SHA256, b"hello world")
print(h.hex())
.. function:: pbkdf2_derive(password: bytes, salt: bytes, iteration_count: int, key_length: int, prf_uri: str) -> bytes
Derive a key using PBKDF2 (RFC 8018).
:param password: The password/secret bytes.
:param salt: Salt bytes.
:param iteration_count: Number of iterations.
:param key_length: Desired output key length in bytes.
:param prf_uri: PRF algorithm URI (e.g. ``Algorithm.HMAC_SHA256``).
:returns: The derived key bytes.
.. code-block:: python
from pybergshamra import pbkdf2_derive, Algorithm
key = pbkdf2_derive(
b"password", b"salt", 100_000, 32, Algorithm.HMAC_SHA256
)
.. function:: hkdf_derive(shared_secret: bytes, key_length: int, prf_uri: str | None = None, salt: bytes | None = None, info: bytes | None = None) -> bytes
Derive a key using HKDF (RFC 5869).
:param shared_secret: Input keying material (IKM).
:param key_length: Desired output key length in bytes.
:param prf_uri: PRF algorithm URI (default: HMAC-SHA256).
:param salt: Optional salt bytes.
:param info: Optional context/info bytes.
:returns: The derived key bytes.
.. code-block:: python
from pybergshamra import hkdf_derive
key = hkdf_derive(b"shared-secret", 32, salt=b"salt", info=b"context")
.. function:: concat_kdf(shared_secret: bytes, key_length: int, digest_uri: str | None = None, algorithm_id: bytes | None = None, party_u_info: bytes | None = None, party_v_info: bytes | None = None) -> bytes
Derive a key using ConcatKDF (NIST SP 800-56A).
:param shared_secret: The shared secret bytes (Z).
:param key_length: Desired output key length in bytes.
:param digest_uri: Digest algorithm URI (default: SHA-256).
:param algorithm_id: Optional AlgorithmID bytes.
:param party_u_info: Optional PartyUInfo bytes.
:param party_v_info: Optional PartyVInfo bytes.
:returns: The derived key bytes.
.. code-block:: python
from pybergshamra import concat_kdf
key = concat_kdf(b"shared-secret", 32)
Certificate validation
----------------------
.. function:: validate_cert_chain(leaf_der: bytes, additional_certs: list[bytes] = [], trusted_certs: list[bytes] = [], untrusted_certs: list[bytes] = [], crls: list[bytes] = [], verification_time: str | None = None, skip_time_checks: bool = False) -> None
Validate an X.509 certificate chain. Verifies that the leaf certificate
chains to a trusted root, optionally checking time validity and CRLs.
:param leaf_der: DER-encoded leaf certificate.
:param additional_certs: Extra certificates from XML (DER-encoded).
:param trusted_certs: Trusted CA certificates (DER-encoded).
:param untrusted_certs: Untrusted intermediate certificates (DER-encoded).
:param crls: Certificate Revocation Lists (DER-encoded).
:param verification_time: Time override (format: ``"YYYY-MM-DD+HH:MM:SS"``).
:param skip_time_checks: Skip NotBefore/NotAfter validation.
:raises CertificateError: If validation fails.
Returns ``None`` on success.
.. code-block:: python
import pybergshamra
leaf_der = open("leaf.der", "rb").read()
ca_der = open("ca.der", "rb").read()
# Raises CertificateError on failure
pybergshamra.validate_cert_chain(
leaf_der,
trusted_certs=[ca_der],
skip_time_checks=True,
)