A small in-process SAML response builder for tests.
The fake IdP does not attempt to model a real admin UI or cryptographic signing pipeline. It builds protocol-correct XML that exercises the SP pipeline, including the signature and assertion parsing paths used by the test suite.
Summary
Functions
The XML-Enc algorithm URIs the encrypt path knows about, as a map.
Wrap a plaintext (typically a genuinely-signed <Assertion> fragment) into an
<EncryptedAssertion> envelope using RSA-OAEP key transport + AES-256-GCM
content encryption against the SP public key, mirroring how sign/2 is the
single canonical signer (D-08).
Adversarial-aware variant of encrypt/2.
Build a complete SAML <Response> binary carrying an <EncryptedAssertion> in
place of a cleartext <Assertion> — the shape ValidationPipeline.run/4 will
consume (Plan 04).
The self-signed certificate PEM (a single-element cert chain) callers must
trust to accept a FakeIdP.sign/2-produced Response. Derived from
keypair/0 via the promoted genuine signer (D-03), so configuring a
connection's cert_chain / idp_certificates with this PEM lets the
verifier accept FakeIdP's real signatures.
Functions
@spec build_response(keyword()) :: Relyra.TestSupport.FakeIdP.Builder.t()
@spec enc_algorithm_uris() :: %{ rsa_oaep: String.t(), aes256_gcm: String.t(), rsa_pkcs1: String.t(), aes256_cbc: String.t() }
The XML-Enc algorithm URIs the encrypt path knows about, as a map.
Plan 04 adversarial fixtures read the blocked variants (:rsa_pkcs1 and
:aes256_cbc) from here so the URI strings live in exactly one place — the
single canonical generator — rather than being re-typed per fixture.
@spec encrypt(binary(), :public_key.rsa_public_key()) :: String.t()
Wrap a plaintext (typically a genuinely-signed <Assertion> fragment) into an
<EncryptedAssertion> envelope using RSA-OAEP key transport + AES-256-GCM
content encryption against the SP public key, mirroring how sign/2 is the
single canonical signer (D-08).
This is the single canonical encrypted-assertion generator — every ENC-01 adversarial fixture (Plan 04) wraps its plaintext through here so a divergent per-test recipe can never mask a real canonicalization or auth-tag bug (Pitfall 1 / T-34-04).
The CipherValue layout is exactly Base.encode64(iv <> ciphertext <> auth_tag)
with a 12-byte IV and a 16-byte GCM tag — the IV(12) || CT || Tag(16) layout
Relyra.Security.XMLEnc.split_cipher_value/1 round-trips (Pitfall 4 /
T-34-05). Feeding the output to XMLEnc.decrypt/3 with the matching SP private
key resolver returns {:ok, plaintext} (byte identity).
Only the SP public key {:RSAPublicKey, n, e} is used here; no private key
material touches the encrypt path (T-34-06). Derive sp_pub_key from
keypair/0 the way xml_enc_test.exs:13-15 does.
@spec encrypt(binary(), :public_key.rsa_public_key(), keyword()) :: String.t()
Adversarial-aware variant of encrypt/2.
Options let Plan 04 fixtures deliberately diverge from the accepted recipe so the SP pipeline can be proven to fail closed:
:key_transport_uri— algorithm URI advertised for the wrapped CEK (defaultrsa-oaep-mgf1p;rsa-1_5for the blocked-PKCS1v1.5 fixture).:key_padding— the:rsa_paddingatom actually used to wrap the CEK (default:rsa_pkcs1_oaep_padding).:content_uri— algorithm URI advertised for content encryption (defaultaes256-gcm;aes256-cbcfor the blocked-CBC fixture).:tag_length— GCM auth-tag byte length (default 16; e.g. 15 for the truncated-tag fixture).:iv— explicit 12-byte IV (default random).:cipher_value_b64— override the content CipherValue text verbatim (e.g. malformed base64 for the bad-ciphertext fixture); skips content encryption.
Build a complete SAML <Response> binary carrying an <EncryptedAssertion> in
place of a cleartext <Assertion> — the shape ValidationPipeline.run/4 will
consume (Plan 04).
The inner <Assertion> is genuinely signed FIRST via the canonical
XmldsigSigner path, THEN encrypted, so the post-decrypt bytes carry the real
DigestValue / SignatureValue the verifier checks (RESEARCH note line 381).
The encrypted <Assertion> (and its sibling <Signature>) carry their own
xmlns="urn:oasis:names:tc:SAML:2.0:assertion" so canonical bytes survive the
decrypt → re-parse → splice (Pitfall 1 / T-34-04).
response_opts are forwarded to the signer (:issuer, :name_id,
:assertion_id, :destination, …); encrypt_opts are forwarded to
encrypt/3 (the adversarial overrides).
@spec keypair() :: term()
@spec metadata() :: String.t()
@spec self_signed_cert_pem() :: String.t()
The self-signed certificate PEM (a single-element cert chain) callers must
trust to accept a FakeIdP.sign/2-produced Response. Derived from
keypair/0 via the promoted genuine signer (D-03), so configuring a
connection's cert_chain / idp_certificates with this PEM lets the
verifier accept FakeIdP's real signatures.