ExSaml.Core.Util (ex_saml v1.0.2)

Copy Markdown View Source

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

build_nsinfo(nsp, rec)

@spec build_nsinfo({:xmlNamespace, default :: term(), nodes :: term()}, term()) ::
  term()

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}.

check_dupe_ets(digest, ttl)

@spec check_dupe_ets(binary(), integer()) :: boolean()

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.

convert_fingerprints(fps)

@spec convert_fingerprints([binary() | charlist()]) :: [binary() | {atom(), binary()}]

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.

datetime_to_saml(arg)

@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"

import_certificate(identifier, pem)

@spec import_certificate(term(), binary()) :: {:ok, binary()} | {:error, term()}

Imports a single X.509 certificate from a PEM-encoded binary string.

Returns the DER-encoded certificate binary.

import_certificate_chain(identifier, pem)

@spec import_certificate_chain(term(), binary()) ::
  {:ok, [binary()]} | {:error, term()}

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.

import_private_key(identifier, pem)

@spec import_private_key(term(), binary()) :: {:ok, term()} | {:error, term()}

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.

load_certificate(path)

@spec load_certificate(String.t()) :: {:ok, binary()} | {:error, term()}

Loads a single X.509 certificate from the given file path.

Returns the DER-encoded certificate binary.

load_certificate_chain(path)

@spec load_certificate_chain(String.t()) :: {:ok, [binary()]} | {:error, term()}

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.

load_metadata(url)

@spec load_metadata(String.t() | charlist()) :: {:ok, binary()} | {:error, term()}

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}.

load_metadata(url, fingerprints)

@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}.

load_private_key(path)

@spec load_private_key(String.t()) :: {:ok, term()} | {:error, term()}

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}.

saml_to_datetime(str)

@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}}

start_ets()

@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.

unique_id()

@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.

xmlAttribute(args \\ [])

(macro)

xmlAttribute(record, args)

(macro)

xmlComment(args \\ [])

(macro)

xmlComment(record, args)

(macro)

xmlDocument(args \\ [])

(macro)

xmlDocument(record, args)

(macro)

xmlElement(args \\ [])

(macro)

xmlElement(record, args)

(macro)

xmlNamespace(args \\ [])

(macro)

xmlNamespace(record, args)

(macro)

xmlPI(args \\ [])

(macro)

xmlPI(record, args)

(macro)

xmlText(args \\ [])

(macro)

xmlText(record, args)

(macro)