<?xml version="1.0" encoding="utf-8"?>
<!-- name="GENERATOR" content="github.com/mmarkdown/mmark Mmark Markdown Processor - mmark.miek.nl" -->
<rfc version="3" ipr="trust200902" docName="draft-ritz-sae-00" submissionType="IETF" category="exp" xml:lang="en" xmlns:xi="http://www.w3.org/2001/XInclude" indexInclude="true">

<front>
<title abbrev="SAE">Static Artifact Exchange (SAE) Protocol</title><seriesInfo value="draft-ritz-sae-00" stream="IETF" status="experimental" name="Internet-Draft"></seriesInfo>
<author fullname="Nathanael Ritz"><organization>Independent</organization><address><postal><street></street>
</postal><email>nathanritz@gmail.com</email></address></author><date year="2025" month="September" day="28"></date>
<area>SEC</area>
<workgroup></workgroup>

<abstract>
<t>This document specifies the Static Artifact Exchange (SAE) protocol,
an asynchronous protocol for exchanging cryptographic artifacts
between two parties via a shared, stateless repository. SAE uses a
pull-only communication model where peers poll for the presence of
immutable, pre-computed artifacts to coordinate a sequenced
exchange. By design, this static artifact model avoids dynamic
request processing, reducing common attack surfaces like injection
and parser vulnerabilities. The protocol is transport-agnostic,
allowing each party to access the repository using different
underlying mechanisms (e.g., cloud APIs or standard HTTPS). SAE is
intended as a foundational transport pattern for protocols like
Ephemeral Compute Attestation (ECA), which require a secure,
minimal channel where trust is derived from the cryptographic
content of the artifacts, not from the channel itself.</t>
</abstract>

</front>

<middle>

<section anchor="sec-intro"><name>Introduction</name>
<t>Many cryptographic protocols require a coordinated, multi-phase
exchange of artifacts like proofs, challenges, or attestation
evidence. Traditional request-response patterns often introduce
security risks at the transport layer, including complex state
management, parser vulnerabilities, and injection attacks.</t>
<t>This document specifies the Static Artifact Exchange (SAE) protocol,
an alternative model designed for security and simplicity. In SAE,
parties do not communicate directly. Instead, they interact
asynchronously through a simple, stateless repository. One party
publishes a set of immutable, pre-computed artifacts for a given
phase and then publishes a status indicator to signal its
completion. The other party polls for this status indicator and,
upon observing it, retrieves the corresponding artifacts.</t>
<t>This &quot;publish-then-poll&quot; pattern reduces the need for active
listeners or dynamic request processing, limiting the attack
surfaces associated with traditional models. The security of the
exchange relies entirely on the cryptographic validity of the
artifacts themselves, not on any feature of the transport mechanism
beyond providing durable storage and reliable retrieval. The
transport is treated as a content-agnostic, key-value store.</t>
<t>SAE is intended as a reusable transport pattern for higher-level
protocols. It is motivated by the requirements of Ephemeral Compute
Attestation (ECA) [I-D.eca-protocol], where a compute instance
must prove its identity before it has been provisioned with any
operational credentials.</t>
</section>

<section anchor="sec-terms"><name>Terminology and Core Concepts</name>
<t>The key words &quot;MUST&quot;, &quot;MUST NOT&quot;, &quot;REQUIRED&quot;, &quot;SHALL&quot;, &quot;SHALL NOT&quot;,
&quot;SHOULD&quot;, &quot;SHOULD NOT&quot;, &quot;RECOMMENDED&quot;, &quot;NOT RECOMMENDED&quot;, &quot;MAY&quot;,
and &quot;OPTIONAL&quot; in this document are to be interpreted as described
in BCP 14 <xref target="RFC2119"></xref> <xref target="RFC8174"></xref> when, and only when, they appear in
all capitals, as shown here.</t>

<dl>
<dt>Peer</dt>
<dd><t>An entity participating in the SAE protocol that can both publish
artifacts to its own repository and retrieve artifacts from its
counterpart's repository.</t>
</dd>
<dt>Artifact</dt>
<dd><t>An immutable blob of data published by a peer, identified by a
predetermined path or address and having a size attribute.</t>
</dd>
<dt>Status Indicator</dt>
<dd><t>A signal published by a peer indicating the completion of a
phase. A zero-byte artifact indicates success, while a
non-zero-byte artifact contains an <strong>HMAC tag</strong> identifying a
specific error (see <eref target="#sec-error-signaling">§ 4.4</eref>).</t>
</dd>
<dt>Phase</dt>
<dd><t>A distinct stage in a protocol where specific artifacts are
exchanged. Phase transitions are atomic and explicitly signaled
(see <eref target="#sec-phase-coordination">§ 4.2</eref>).</t>
</dd>
<dt>Repository</dt>
<dd><t>A durable, addressable, and immutable store for artifacts. It
provides a means to publish and retrieve artifacts but does not
provide any protocol-level intelligence.</t>
</dd>
<dt>Exchange Identifier</dt>
<dd><t>A unique identifier (e.g., a UUID) for a specific exchange
instance, used to construct artifact paths. The generation and
out-of-band communication of the Exchange Identifier is the
responsibility of the higher-layer protocol utilizing SAE. <em>In
the ECA profile, this corresponds to the <tt>eca_uuid</tt></em> (ECA Terms
<eref target="https://datatracker.ietf.org/doc/draft-ritz-eca-00/">§ 2.1</eref>).</t>
</dd>
<dt>Inhomogeneous Transport Fabric</dt>
<dd><t>A design pattern enabled by SAE where peers operate over different
transport mechanisms (e.g., one peer uses a cloud API, the other
uses HTTPS) to access the same repository. This decoupling allows
each peer to maintain its own network topology and security
posture without constraining its counterpart.</t>
</dd>
</dl>
</section>

<section anchor="sec-invariants"><name>Protocol Requirements (Normative)</name>
<t>SAE implementations MUST satisfy the following invariants:</t>

<ol type="%d.">
<li><t><strong>Static Artifact Model</strong>: All communication MUST conform to a
strict artifact exchange model where both peers operate as
passive repositories of pre-computed, immutable artifacts.</t>
</li>
<li><t><strong>Pull-Only Communication</strong>: Peers MUST NOT push data. All
artifact retrieval MUST be initiated by the consuming peer
through polling predetermined paths.</t>
</li>
<li><t><strong>Phased Atomic Progression</strong>: The protocol MUST progress
through distinct phases. All artifacts for a phase MUST be
published before its corresponding status indicator is published.</t>
</li>
<li><t><strong>Immutability</strong>: Once a status indicator for a phase is
published, all associated artifacts for that phase MUST NOT be
changed or removed.</t>
</li>
<li><t><strong>Prohibited Processing</strong>: The protocol's state transitions and
security decisions MUST be based on the presence and size of
artifacts, not their content. A non-zero Content-Length for a
status indicator MUST be treated as a terminal failure, ending
the exchange. Implementations MUST NOT parse or interpret
arbitrary or variable-length content to determine the protocol's
outcome. The inspection of a status indicator's content to
diagnose a failure is an operational concern that MUST only
occur after the exchange has already been terminated based on
the artifact's size.</t>
</li>
<li><t><strong>Bounded Polling</strong>: Polling for status indicators MUST use
exponential backoff.</t>
</li>
<li><t><strong>Transport Simplicity</strong>: The protocol logic MUST remain
independent of transport-level features beyond basic artifact
retrieval (e.g., HTTP GET/HEAD) and presence signaling (e.g.,
HTTP 200/404 status codes). Implementations MUST treat the
transport layer as a stateless key-value store and MUST NOT
derive protocol behavior from any other transport metadata, such
as HTTP headers or TLS session details.</t>
</li>
<li><t><strong>Repository Consistency</strong>: Any repository used to host
artifacts MUST provide strong read-after-write consistency for
all operations. Eventually consistent systems MUST NOT be used
unless specifically configured to provide strong consistency
guarantees for the relevant objects.</t>
</li>
<li><t><strong>Resilient Artifact Retrieval</strong>: After a peer successfully
observes the presence of a status indicator, it proceeds to
retrieve the corresponding phase artifacts. The peer's retrieval
logic MUST be resilient to transient transport- or storage-layer
delays. It is RECOMMENDED that implementations employ a bounded
retry mechanism with exponential backoff when fetching artifacts.
The initial observation of the status indicator is sufficient to
proceed with the retrieval attempts; re-verification of the
status indicator during a retry loop is NOT RECOMMENDED as it
adds unnecessary complexity.</t>
</li>
</ol>
<blockquote><t>NOTE: SAE's model enables an <em>&quot;inhomogeneous transport fabric&quot;</em>,
where peers need not share a common protocol beyond basic
retrieval semantics. For example, one peer might access the
repository via a cloud provider's SDK for low-latency internal
operations, while another uses HTTPS over a bastion host. This
decouples endpoint security postures, supporting hardened,
outbound-only configurations without sacrificing usability.</t>
</blockquote></section>

<section anchor="sec-mechanics"><name>Protocol Mechanics</name>

<section anchor="sec-repo-structure"><name>Artifact Repository Structure</name>
<t>Each peer MUST organize its repository using a consistent path
structure based on the Exchange Identifier.</t>
<t><strong>Recommended Structure</strong>:</t>

<artwork><![CDATA[/<exchange_id>/<artifact_name>
/<exchange_id>/<phase_name>.status
]]></artwork>
<t>Where <tt>exchange_id</tt> uniquely identifies the exchange,
<tt>artifact_name</tt> identifies a specific artifact, and <tt>phase_name</tt>
identifies the status indicator for a phase.</t>
</section>

<section anchor="sec-phase-coordination"><name>Phase Coordination</name>
<t>The protocol progresses using the following pattern:</t>

<ol type="%d.">
<li><t><strong>Publication</strong>: A peer publishes all required artifacts for the
current phase to its repository.</t>
</li>
<li><t><strong>Signaling</strong>: After all artifacts are published, the peer
publishes the status indicator for that phase.</t>

<artwork><![CDATA[ * **Success**: The status indicator MUST be a zero-byte
artifact (e.g., an empty file) whose presence alone signals
successful completion of the phase.
 * **Failure**: The status indicator MUST be a non-zero-byte
artifact containing an **HMAC tag** as defined in
[§ 4.4](#sec-error-signaling).
]]></artwork>
</li>
<li><t><strong>Polling</strong>: The counterpart peer polls for the status indicator
using transport-appropriate presence checks (e.g., HTTP <tt>HEAD</tt>
requests) with bounded retry logic (see
<eref target="#sec-invariants">§ 3</eref>).</t>
</li>
<li><t><strong>Retrieval and Interpretation</strong>: Upon receiving a response to
the presence check:</t>

<artwork><![CDATA[ * If the artifact is absent (e.g., HTTP 404 Not Found), the
phase is not complete; continue polling.

 * If the artifact is present:


  * If the size is zero, the phase was successful; proceed
    to retrieve the associated phase artifacts.
  * If the size is greater than zero, the phase has failed.
    The peer MUST immediately consider the exchange to be in
    a terminal failure state. Identifying the specific cause
    of the failure by inspecting the artifact's content, as
    described in [§ 4.5](#sec-error-diagnosis), is an
    OPTIONAL step that may be performed for diagnostic
    purposes after termination.
]]></artwork>
</li>
</ol>
<t>The publication of a phase's status indicator MUST be an atomic
operation from an observer's perspective. After all other artifacts
for that phase are durable and fully written, the status indicator
MUST appear instantly and completely. Implementations MUST NOT
allow an observer to view a status indicator in a partially written
or otherwise inconsistent state.</t>
</section>

<section anchor="sec-transport"><name>Transport Abstraction</name>
<t>While SAE is transport-agnostic in principle, HTTPS <bcp14>MUST</bcp14> be
implemented as the mandatory-to-implement transport. The transport
layer MUST provide:</t>

<ol spacing="compact" type="%d.">
<li>Retrieval operations (e.g., HTTP <tt>GET</tt>, <tt>HEAD</tt>)</li>
<li>Presence/absence signaling (e.g., HTTP <tt>200</tt>/<tt>404</tt> status codes)</li>
<li>Confidentiality and integrity (e.g., TLS 1.2 or later)</li>
</ol>
<t>Transport-level metadata (e.g., custom headers) <bcp14>MUST NOT</bcp14>
influence protocol behavior. The method used to publish artifacts
to a repository is considered an implementation detail; SAE does
not constrain publisher semantics (e.g., HTTP <tt>PUT</tt>, <tt>rsync</tt>, or
object-store APIs are all acceptable).</t>
</section>

<section anchor="sec-error-signaling"><name>Error Signaling</name>
<t>When a peer encounters an application-level error, it MUST publish
a status indicator artifact containing a Keyed-Hash Message
Authentication Code (<tt>HMAC-SHA256</tt>). The HMAC tag MUST be computed
over the concatenation of the Exchange Identifier and the canonical
error string (e.g., <tt>exchange_id || &quot;:&quot; || error</tt>). The key used
for the HMAC MUST be derived from a secret shared between the peers
out-of-band or established by the higher-layer protocol. This binds
the error signal to a specific exchange context, preventing replay
attacks across different exchanges and ensuring an unauthorized
party cannot forge a valid error signal. <em>(In ECA, these canonical
error strings are defined in the ECA Error Codes registry; see ECA
<eref target="https://datatracker.ietf.org/doc/draft-ritz-eca-00/">§ 12.2.1</eref>.)</em></t>
</section>

<section anchor="sec-error-diagnosis"><name>Error Cause Diagnosis (Optional)</name>
<t>The presence of any non-zero-byte content in a status indicator
signifies a terminal failure of the exchange. For operational
hardening, consuming peers <bcp14>MUST</bcp14> first check the
<tt>Content-Length</tt> header.</t>
</section>

<section anchor="sec-artifact-lifecycle"><name>Artifact Lifecycle Management (Informative)</name>
<t>In large-scale deployments, operators SHOULD implement a garbage
collection strategy to remove artifacts from completed or timed-out
exchanges to prevent storage sprawl. A common approach is to apply
a time-to-live (TTL) policy to all artifacts associated with an
<tt>exchange_id</tt>, deleting them after a defined period (e.g., 24
hours) has passed since the exchange was initiated.</t>
</section>
</section>

<section anchor="sec-security"><name>Security Considerations</name>

<section anchor="sec-security-processing"><name>Elimination of Processing Vulnerabilities</name>
<t>The static artifact model makes implementations inherently immune
to injection attacks, parser vulnerabilities, and other flaws
common in request-processing systems. Since peers never process
arbitrary client-supplied data, these attack vectors are eliminated
by design. The use of fixed-size <strong>HMAC tag verification</strong> further
reduces risk by avoiding the parsing of arbitrary error messages.</t>
</section>

<section anchor="sec-security-race"><name>Prevention of Race Conditions</name>
<t>The atomic &quot;publish-then-signal&quot; model prevents
Time-of-Check-to-Time-of-Use (TOCTOU) vulnerabilities. A peer that
detects a status indicator is guaranteed that all associated
artifacts are already published and immutable.</t>
</section>

<section anchor="sec-security-dos"><name>Resilience Against Denial of Service</name>
<t>The stateless nature of the repository model provides resilience
against DoS attacks that target session state. The bounded polling
requirement (see <eref target="#sec-invariants">§ 3</eref>) further mitigates resource
exhaustion from misbehaving peers.</t>
</section>

<section anchor="sec-security-transport"><name>Transport Security</name>
<t>Implementations <bcp14>MUST</bcp14> use transport-layer encryption to protect
artifacts in transit.</t>
</section>

<section anchor="sec-security-error-signaling"><name>Error Signaling</name>
<t>The use of authenticated, fixed-size error codes within status
indicators upholds the <strong>Prohibited Processing</strong> invariant by
avoiding the need to parse arbitrary or variable-length error
messages. A peer makes its terminal failure decision based on size
alone. This approach contrasts with protocols that rely on
variable-length messages, which can introduce parsing overhead and
potential vulnerabilities in resource-constrained environments.</t>
</section>

<section anchor="sec-info-leak"><name>Information Leakage via Error Hashes</name>
<t>Using predictable, public hashes for error signaling supports
interoperability and debugging but allows a passive repository
observer to gain real-time intelligence on failures. The primary
mitigation is strict repository access control. In high-security
environments, repositories SHOULD NOT be publicly observable.</t>
</section>

<section anchor="sec-replay"><name>Resilience to Replay Attacks</name>
<t>Replay protection across exchanges MUST be provided by the
higher-layer protocol (e.g., ECA’s accept-once semantics for
<tt>eca_uuid</tt>; see ECA [I-D.eca-protocol] §4.1 (<em>Validation Gates</em>)
{#sec-validation-gates}). Operators SHOULD implement repository
controls (path-based write permissions, audit logging) to mitigate
local replay by a writer.</t>
</section>
</section>

<section anchor="sec-iana"><name>IANA Considerations</name>

<section anchor="sae-error-codes-registry"><name>SAE Error Codes Registry</name>
<t>IANA is requested to establish a registry for SAE Error Codes. This
registry defines the error code identifier and the canonical string
that, when concatenated with the Exchange Identifier, is used as
input to the HMAC function for error signaling. Implementations
MUST compute the HMAC tag over the exact bytes of the canonical
content with no trailing newline.</t>
<table>
<thead>
<tr>
<th align="left">Code</th>
<th align="left">Canonical Content (UTF-8)</th>
<th align="left">Description (Analogy)</th>
</tr>
</thead>

<tbody>
<tr>
<td align="left"><tt>BAD_REQUEST</tt></td>
<td align="left"><tt>BAD_REQUEST</tt></td>
<td align="left">A client-side error due to a malformed or invalid artifact (HTTP 400).</td>
</tr>

<tr>
<td align="left"><tt>UNAUTHORIZED</tt></td>
<td align="left"><tt>UNAUTHORIZED</tt></td>
<td align="left">Authentication is required and has failed or has not yet been provided (HTTP 401).</td>
</tr>

<tr>
<td align="left"><tt>FORBIDDEN</tt></td>
<td align="left"><tt>FORBIDDEN</tt></td>
<td align="left">The server understood the request but refuses to authorize it (HTTP 403).</td>
</tr>

<tr>
<td align="left"><tt>CONFLICT</tt></td>
<td align="left"><tt>CONFLICT</tt></td>
<td align="left">The request could not be completed due to a conflict with the current state (HTTP 409).</td>
</tr>

<tr>
<td align="left"><tt>GATEWAY_TIMEOUT</tt></td>
<td align="left"><tt>GATEWAY_TIMEOUT</tt></td>
<td align="left">A peer did not receive a timely response from its counterpart (HTTP 504).</td>
</tr>
</tbody>
</table></section>
</section>

<section anchor="sec-refs-norm"><name>Normative References</name>

<dl spacing="compact">
<dt>[I-D.eca-protocol]</dt>
<dd>Ritz, N., &quot;Ephemeral Compute Attestation (ECA) Protocol&quot;, Work in
Progress, draft-ritz-eca-00, 16 September 2025.</dd>
<dt><xref target="RFC2119"></xref></dt>
<dd>Bradner, S., &quot;Key words for use in RFCs to Indicate Requirement
Levels&quot;, BCP 14, RFC 2119.</dd>
<dt><xref target="RFC6234"></xref></dt>
<dd>Eastlake 3rd, D. and T. Hansen, &quot;US Secure Hash Algorithms (SHA
and SHA-based HMAC and HKDF)&quot;, RFC 6234, DOI 10.17487/RFC6234,
May 2011, <eref target="https://www.rfc-editor.org/info/rfc6234">https://www.rfc-editor.org/info/rfc6234</eref>.</dd>
<dt><xref target="RFC8174"></xref></dt>
<dd>Leiba, B., &quot;Ambiguity of Uppercase vs Lowercase in RFC 2119 Key
Words&quot;, BCP 14, RFC 8174.</dd>
<dt><xref target="RFC8446"></xref></dt>
<dd>Rescorla, E., &quot;The Transport Layer Security (TLS) Protocol
Version 1.3&quot;, RFC 8446, DOI 10.17487/RFC8446, August 2018.</dd>
<dt><xref target="RFC9110"></xref></dt>
<dd>Fielding, R., Ed., Nottingham, M., Ed., and J. Reschke, Ed.,
&quot;HTTP Semantics&quot;, STD 97, RFC 9110, DOI 10.17487/RFC9110, June
2022.</dd>
</dl>
</section>

</middle>

<back>
<references><name>Informative References</name>
<xi:include href="https://bib.ietf.org/public/rfc/bibxml/reference.RFC.2119.xml"/>
<xi:include href="https://bib.ietf.org/public/rfc/bibxml/reference.RFC.6234.xml"/>
<xi:include href="https://bib.ietf.org/public/rfc/bibxml/reference.RFC.8174.xml"/>
<xi:include href="https://bib.ietf.org/public/rfc/bibxml/reference.RFC.8446.xml"/>
<xi:include href="https://bib.ietf.org/public/rfc/bibxml/reference.RFC.9110.xml"/>
</references>

<section anchor="appendix-a"><name>HTTPS Profile (Normative)</name>
<t>This section provides a normative example of how the abstract SAE
protocol can be implemented using standard HTTPS over a repository
(e.g., a file system or an object store).</t>

<section anchor="appendix-a-transport"><name>Artifact Transport</name>

<ul spacing="compact">
<li><strong>Retrieval</strong>: Use HTTP <tt>GET</tt> to retrieve artifacts and HTTP
<tt>HEAD</tt> to check for their presence and size.</li>
<li><strong>Presence/Absence Signaling</strong>: A successful HTTP <tt>200</tt> response
indicates presence, while a <tt>404</tt> indicates absence.</li>
<li><strong>Success/Failure Signaling</strong>: A zero-byte artifact's
<tt>Content-Length: 0</tt> header indicates success. Any
<tt>Content-Length</tt> greater than zero indicates a terminal failure.</li>
</ul>
</section>

<section anchor="appendix-a-example"><name>Example Exchange</name>
<t>This example illustrates a simple two-phase SAE exchange between
peers A and B using the exchange identifier <tt>exchange-12345</tt>.</t>
<t><strong>Phase 1: A publishes an initial proof artifact</strong></t>

<artwork><![CDATA[A: PUT /exchange-12345/proof.json
A: PUT /exchange-12345/proof.status (0 bytes)
B: HEAD /exchange-12345/proof.status (polling until 200 OK)
B: Observes Content-Length: 0 → success
B: GET /exchange-12345/proof.json
]]></artwork>
<t><strong>Error example:</strong> If B encounters a timeout while waiting for A,
it publishes an authenticated error signal.</t>

<artwork><![CDATA[# B derives the HMAC key from a shared secret
# B computes the HMAC tag for the "GATEWAY_TIMEOUT" string
# B hex-encodes the 32-byte tag into a 64-character string
B: PUT /exchange-12345/response.status (content: <64-char-hex-hmac-tag>)
A: HEAD /exchange-12345/response.status (polling until 200 OK)
A: Observes Content-Length > 0 // Exchange is dead
A: GET /exchange-12345/response.status (reads 64 bytes) // OPTIONAL
A: Verifies received tag against its own computed HMACs for known errors
A: Finds match for GATEWAY_TIMEOUT -> aborts exchange
]]></artwork>
</section>

<section anchor="appendix-a-impl"><name>Implementation with Standard Web Servers</name>

<sourcecode type="nginx"><![CDATA[server {
    listen 443 ssl;
    root /var/sae/repository;
    location / {
        try_files $uri =404;
        add_header Cache-Control "private, max-age=0, immutable";
    }
}
]]></sourcecode>

<sourcecode type="bash"><![CDATA[#!/bin/bash
EXCHANGE_ID="$1"
PHASE_NAME="$2" # Name of the phase (e.g., "proof")
ERROR_CODE="${3:-}" # If set, indicates failure and specifies error code

# The HMAC key MUST be securely provided (e.g., via env var or file)
# This is an example and not a secure way to handle keys.
HMAC_KEY="${SAE_HMAC_KEY}"

REPO_ROOT="/var/sae/repository"
PHASE_DIR="$REPO_ROOT/$EXCHANGE_ID"
mkdir -p "$PHASE_DIR"

# ... (script logic to publish main artifacts for the phase) ...

# For the status indicator, creation is the signal.
if [[ -z "$ERROR_CODE" ]]; then
    # Success: create an empty status file
    touch "$PHASE_DIR/$PHASE_NAME.status"
else
    # Failure: create a status file with the HMAC of the error code
    TMPSTATUS=$(mktemp -p "$PHASE_DIR")
    # Calculate the HMAC tag of the error string and hex-encode it
    printf "%s" "${EXCHANGE_ID}":"${ERROR_CODE}" | openssl dgst -sha256 -hmac "$HMAC_KEY" -r | awk '{print $1}' > "$TMPSTATUS"
    mv -f "$TMPSTATUS" "$PHASE_DIR/$PHASE_NAME.status"
fi
]]></sourcecode>
</section>
</section>

<section anchor="appendix-b"><name>Verifying Error Signals (Informative)</name>
<t>This appendix provides a helper script that can be used to diagnose
a failed exchange by verifying a received HMAC tag against the set
of known canonical error strings.</t>

<sourcecode type="bash"><![CDATA[#!/usr/bin/env bash
# verify-sae-error.sh: Verifies a received HMAC tag to identify an error.

RECEIVED_TAG="$1"

if [[ -z "$RECEIVED_TAG" ]]; then
  echo "Usage: $0 <received-hmac-tag>"
  exit 1
fi

# The HMAC key MUST be the same one used by the publishing peer.
HMAC_KEY="${SAE_HMAC_KEY}"

if [[ -z "$HMAC_KEY" ]]; then
  echo "Error: SAE_HMAC_KEY environment variable not set."
  exit 1
fi

# Array of known canonical error strings from the IANA registry
declare -a KNOWN_ERRORS=(
  "BAD_REQUEST"
  "UNAUTHORIZED"
  "FORBIDDEN"
  "CONFLICT"
  "GATEWAY_TIMEOUT"
  # ... Higher-layer protocol errors would be added here
)

for ERROR_STRING in "${KNOWN_ERRORS[@]}"; do
  # Recompute the expected HMAC tag for this error string
  EXPECTED_TAG=$(printf "%s" "$EXCHANGE_ID" "$ERROR_STRING" | openssl dgst -sha256 -hmac "$HMAC_KEY" -r | awk '{print $1}')

  if [[ "$RECEIVED_TAG" == "$EXPECTED_TAG" ]]; then
    echo "VERIFIED: The error code is '$ERROR_STRING'."
    exit 0
  fi
done

echo "UNKNOWN_ERROR: The received tag does not match any known error code."
exit 1
]]></sourcecode>
</section>

</back>

</rfc>
