Utility functions for SAML processing.
Pure Elixir port of the Erlang esaml_util module. Provides helpers for
datetime conversion, unique ID generation, fingerprint handling, xmerl
namespace processing, key/certificate loading, metadata fetching, and
duplicate-assertion checking.
Note: the original Erlang helpers thread/2, threaduntil/2, and
folduntil/3 are intentionally not ported — use Elixir's with, pipes,
and Enum.reduce_while/3 instead.
Summary
Functions
Recursively adds namespace info (nsinfo field) to xmerl element and
attribute records.
Checks whether the given assertion digest has been seen before.
Converts a list of fingerprints in various formats into normalised binaries.
Converts an Erlang datetime tuple to a SAML 2.0 UTC timestamp string.
Imports a single X.509 certificate from a PEM-encoded binary string.
Imports a certificate chain from a PEM-encoded binary string.
Imports an RSA private key from a PEM-encoded binary string.
Loads a single X.509 certificate from the given file path.
Loads a certificate chain from the given file path.
Fetches IdP metadata XML from the given URL via :httpc.
Fetches IdP metadata XML from the given URL and verifies its XML signature using the provided fingerprints.
Loads an RSA private key from the given file path.
Parses a SAML 2.0 UTC timestamp string into an Erlang datetime tuple.
No-op kept for backwards compatibility.
Generates a unique SAML ID string (hex-encoded, prefixed with underscore).
Functions
Recursively adds namespace info (nsinfo field) to xmerl element and
attribute records.
Takes an xmlNamespace record and an xmerl node. For xmlElement and
xmlAttribute records whose names contain a colon (i.e. namespace-prefixed),
the nsinfo field is set to {prefix, local_name}.
Checks whether the given assertion digest has been seen before.
If digest is new, it is inserted into the :ex_saml_core_assertion_seen
ETS table and false is returned. If it already exists, returns true.
This is a simplified local-only version — the original Erlang implementation
used rpc:multicall across all nodes.
Converts a list of fingerprints in various formats into normalised binaries.
Accepted input formats per element:
- Hex colon-separated string:
"AA:BB:CC:DD:..." - Typed (with digest prefix):
"SHA256:base64data"or"SHA:base64data" - Raw binary (already decoded)
Returns a list of binary() or {atom(), binary()} tuples.
@spec datetime_to_saml(:calendar.datetime()) :: String.t()
Converts an Erlang datetime tuple to a SAML 2.0 UTC timestamp string.
Examples
iex> ExSaml.Core.Util.datetime_to_saml({{2013, 5, 2}, {17, 26, 53}})
"2013-05-02T17:26:53Z"
Imports a single X.509 certificate from a PEM-encoded binary string.
Returns the DER-encoded certificate binary.
Imports a certificate chain from a PEM-encoded binary string.
Returns a list of DER-encoded certificate binaries. Results are cached in
the :ex_saml_core_certbin_cache ETS table under the given identifier.
Imports an RSA private key from a PEM-encoded binary string.
The key is cached in the :ex_saml_core_privkey_cache ETS table under
the given identifier.
Loads a single X.509 certificate from the given file path.
Returns the DER-encoded certificate binary.
Loads a certificate chain from the given file path.
Returns a list of DER-encoded certificate binaries. Results are cached in
the :ex_saml_core_certbin_cache ETS table.
Fetches IdP metadata XML from the given URL via :httpc.
The raw XML binary is cached in the :ex_saml_core_idp_meta_cache ETS table.
Returns {:ok, xml_binary} or {:error, reason}.
@spec load_metadata(String.t() | charlist(), [binary() | {atom(), binary()}]) :: {:ok, binary()} | {:error, term()}
Fetches IdP metadata XML from the given URL and verifies its XML signature using the provided fingerprints.
This is a structural port — signature verification will be refined later.
Returns {:ok, xml_binary} or {:error, reason}.
Loads an RSA private key from the given file path.
The key is cached in the :ex_saml_core_privkey_cache ETS table.
Returns {:ok, rsa_private_key} or {:error, reason}.
@spec saml_to_datetime(binary() | charlist()) :: :calendar.datetime()
Parses a SAML 2.0 UTC timestamp string into an Erlang datetime tuple.
Accepts both binary strings and charlists.
Examples
iex> ExSaml.Core.Util.saml_to_datetime("2013-05-02T17:26:53Z")
{{2013, 5, 2}, {17, 26, 53}}
@spec start_ets() :: :ok
No-op kept for backwards compatibility.
ETS tables are managed by ExSaml.Core.TableOwner. This function verifies
that the expected tables exist and returns :ok.
@spec unique_id() :: String.t()
Generates a unique SAML ID string (hex-encoded, prefixed with underscore).
Uses :crypto.strong_rand_bytes/1 for cryptographic randomness.