# `ExSaml.Core.Util`
[🔗](https://github.com/docJerem/ex_saml/blob/main/lib/ex_saml/core/util.ex#L1)

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.

# `build_nsinfo`

```elixir
@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`

```elixir
@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`

```elixir
@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`

```elixir
@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`

```elixir
@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`

```elixir
@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`

```elixir
@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`

```elixir
@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`

```elixir
@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`

```elixir
@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`

```elixir
@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`

```elixir
@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`

```elixir
@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`

```elixir
@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`

```elixir
@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`
*macro* 

# `xmlAttribute`
*macro* 

# `xmlComment`
*macro* 

# `xmlComment`
*macro* 

# `xmlDocument`
*macro* 

# `xmlDocument`
*macro* 

# `xmlElement`
*macro* 

# `xmlElement`
*macro* 

# `xmlNamespace`
*macro* 

# `xmlNamespace`
*macro* 

# `xmlPI`
*macro* 

# `xmlPI`
*macro* 

# `xmlText`
*macro* 

# `xmlText`
*macro* 

---

*Consult [api-reference.md](api-reference.md) for complete listing*
