<?xml version='1.0' encoding='utf-8'?>
<!DOCTYPE rfc [
  <!ENTITY nbsp    "&#160;">
  <!ENTITY zwsp   "&#8203;">
  <!ENTITY nbhy   "&#8209;">
  <!ENTITY wj     "&#8288;">
]>
<?xml-stylesheet type="text/xsl" href="rfc2629.xslt" ?>
<!-- generated by https://github.com/cabo/kramdown-rfc version 1.7.29 (Ruby 3.4.4) -->
<rfc xmlns:xi="http://www.w3.org/2001/XInclude" ipr="trust200902" docName="draft-denis-uricrypt-03" category="info" submissionType="independent" tocInclude="true" sortRefs="true" symRefs="true" version="3">
  <!-- xml2rfc v2v3 conversion 3.30.2 -->
  <front>
    <title abbrev="URICrypt">Prefix-Preserving Encryption for URIs</title>
    <seriesInfo name="Internet-Draft" value="draft-denis-uricrypt-03"/>
    <author initials="F." surname="Denis" fullname="Frank Denis">
      <organization>Fastly Inc.</organization>
      <address>
        <email>fde@00f.net</email>
      </address>
    </author>
    <date year="2025"/>
    <keyword>Internet-Draft</keyword>
    <abstract>
      <?line 19?>

<t>This document specifies URICrypt, a deterministic, prefix-preserving
encryption scheme for Uniform Resource Identifiers (URIs). URICrypt
encrypts URI paths while preserving their hierarchical structure,
enabling systems that rely on URI prefix relationships to continue
functioning with encrypted URIs. The scheme provides authenticated
encryption for each URI path component, preventing tampering,
reordering, or mixing of encrypted segments.</t>
    </abstract>
    <note removeInRFC="true">
      <name>Discussion Venues</name>
      <t>Source for this draft and an issue tracker can be found at
    <eref target="https://github.com/jedisct1/draft-denis-uricrypt"/>.</t>
    </note>
  </front>
  <middle>
    <?line 29?>

<section anchor="introduction">
      <name>Introduction</name>
      <t>This document specifies URICrypt, a method for encrypting Uniform
Resource Identifiers (URIs) while preserving their hierarchical
structure. The primary motivation is to enable systems that rely on
URI prefix relationships for routing, filtering, or access control to
continue functioning with encrypted URIs.</t>
      <t>URICrypt achieves prefix preservation through a chained encryption
model where the encryption of each URI component depends
cryptographically on all preceding components. This ensures that URIs
sharing common prefixes produce ciphertexts that also share common
encrypted prefixes.</t>
      <t>The scheme uses an extendable-output function (XOF) as its cryptographic primitive
and provides authenticated encryption for each component, preventing
tampering, reordering, or mixing of encrypted segments. URICrypt is a
reversible encryption scheme: encrypted URIs can be fully decrypted to
recover the original URIs, but only with possession of the secret key.</t>
      <section anchor="use-cases-and-motivations">
        <name>Use Cases and Motivations</name>
        <t>The main motivations include:</t>
        <ul spacing="normal">
          <li>
            <t>Access Control in CDNs: Content Delivery Networks often use URI
prefixes for routing and access control. URICrypt allows encryption of
resource paths while preserving the prefix structure needed for
CDN operations.</t>
          </li>
          <li>
            <t>Privacy-Preserving Logging: Systems can log encrypted URIs
without exposing sensitive path information, while still enabling
analysis based on URI structure.</t>
          </li>
          <li>
            <t>Confidential Data Sharing: When sharing links to sensitive
resources, URICrypt prevents the path structure itself from
revealing confidential information.</t>
          </li>
          <li>
            <t>Token-Based Access Systems: Systems that issue time-limited
access tokens can use URICrypt to obfuscate the underlying
resource location while maintaining routability.</t>
          </li>
          <li>
            <t>Multi-tenant Systems: In systems where multiple tenants share
infrastructure, URICrypt can isolate tenant data while allowing
shared components to be processed efficiently.</t>
          </li>
          <li>
            <t>Privacy-preserving Analytics: URICrypt can complement IPCrypt
<xref target="I-D.draft-denis-ipcrypt"/>. Together, they enable systems to perform
analytics on encrypted network flows and resource access patterns
without exposing sensitive information about either the network
endpoints or the specific resources being accessed.</t>
          </li>
        </ul>
      </section>
    </section>
    <section anchor="terminology">
      <name>Terminology</name>
      <t>The key words “MUST”, “MUST NOT”, “REQUIRED”, “SHALL”, “SHALL NOT”,
“SHOULD”, “SHOULD NOT”, “RECOMMENDED”, “NOT RECOMMENDED”, “MAY”, and
“OPTIONAL” in this document are to be interpreted as described in BCP
14 <xref target="RFC2119"/> <xref target="RFC8174"/> when, and only when, they appear in all capitals, as
shown here.</t>
      <t>Throughout this document, the following terms and conventions apply:</t>
      <ul spacing="normal">
        <li>
          <t>URI: Uniform Resource Identifier as defined in <xref target="RFC3986"/>.</t>
        </li>
        <li>
          <t>URI Component: A segment of a URI path, terminated by ‘/’, ‘?’, or
‘#’ characters. For encryption purposes, components include the
trailing terminator except for the final component.</t>
        </li>
        <li>
          <t>Scheme: The URI scheme (e.g., “https://”) which is preserved in
plaintext.</t>
        </li>
        <li>
          <t>XOF: Extendable-Output Function, a cryptographic function that can
produce output of arbitrary length.</t>
        </li>
        <li>
          <t>SIV: Synthetic Initialization Vector, a value derived from the
accumulated state of all previous components, used for
authentication and as input to keystream generation.</t>
        </li>
        <li>
          <t>SIVLEN: The length of the Synthetic Initialization Vector in bytes,
defined as 16 bytes (128 bits) for this specification.</t>
        </li>
        <li>
          <t>PADBS: Padding Block Size, the number of bytes to which ciphertext
components are aligned. Defined as 3 bytes for this specification
to ensure efficient Base64url encoding without padding characters.</t>
        </li>
        <li>
          <t>Domain Separation: The practice of using distinct inputs to
cryptographic functions to ensure outputs for different purposes
are not compatible.</t>
        </li>
        <li>
          <t>Prefix-preserving Encryption: An encryption scheme where, if two
plaintexts share a common prefix, their corresponding ciphertexts
also share a common (encrypted) prefix.</t>
        </li>
        <li>
          <t>Chained Encryption: A mode where encryption of each component
depends cryptographically on all preceding components.</t>
        </li>
      </ul>
    </section>
    <section anchor="uri-processing">
      <name>URI Processing</name>
      <t>This section describes how URIs are processed for encryption and
decryption.</t>
      <t>The overall encryption flow transforms a plaintext URI into an encrypted
URI while preserving its hierarchical structure:</t>
      <artwork><![CDATA[
+-------------------------------------------------------------+
|                         Input URI                           |
|          "https://example.com/path/to/resource"             |
+-------------------------------------------------------------+
                              |
                              v
+-------------------------------------------------------------+
|                    URI Decomposition                        |
+-------------------------------------------------------------+
|  Scheme: "https://"                                         |
|  Components: ["example.com/", "path/", "to/", "resource"]   |
+-------------------------------------------------------------+
                              |
                              v
+-------------------------------------------------------------+
|                 Chained Encryption Process                  |
+-------------------------------------------------------------+
|  For each component in sequence:                            |
|    1. Update state with plaintext                           |
|    2. Generate SIV from accumulated state                   |
|    3. Derive keystream using SIV                            |
|    4. Encrypt component with keystream                      |
|    5. Output: SIV || encrypted_component                    |
+-------------------------------------------------------------+
                              |
                              v
+-------------------------------------------------------------+
|                    Encoding & Assembly                      |
+-------------------------------------------------------------+
|  1. Concatenate all (SIV || encrypted_component) pairs      |
|  2. Apply base64url encoding                                |
|  3. Prepend original scheme                                 |
+-------------------------------------------------------------+
                              |
                              v
+-------------------------------------------------------------+
|                       Encrypted URI                         |
|          "https://HOGo9vauZ3b3xsPNPQng5apS..."              |
+-------------------------------------------------------------+
]]></artwork>
      <section anchor="uri-component-extraction">
        <name>URI Component Extraction</name>
        <t>Before encryption, a URI must be split into its scheme and path
components. The path is further divided into individual components for
chained encryption. Components are terminated by ‘/’, ‘?’, or ‘#’
characters, which allows proper handling of query strings and fragments.</t>
        <section anchor="full-uris">
          <name>Full URIs</name>
          <t>For a full URI including a scheme:</t>
          <artwork><![CDATA[
Input:  "https://example.com/a/b/c"

Components:

- Scheme: "https://"
- Component 1: "example.com/"
- Component 2: "a/"
- Component 3: "b/"
- Component 4: "c"
]]></artwork>
          <t>For a URI with query parameters:</t>
          <artwork><![CDATA[
Input:  "https://example.com/path?foo=bar&baz=qux"

Components:

- Scheme: "https://"
- Component 1: "example.com/"
- Component 2: "path?"
- Component 3: "foo=bar&baz=qux"
]]></artwork>
          <t>For a URI with a fragment:</t>
          <artwork><![CDATA[
Input:  "https://example.com/path#section"

Components:

- Scheme: "https://"
- Component 1: "example.com/"
- Component 2: "path#"
- Component 3: "section"
]]></artwork>
          <t>Note that all components except the last include their trailing terminator
character (‘/’, ‘?’, or ‘#’). This ensures proper reconstruction during decryption.</t>
        </section>
        <section anchor="path-only-uris">
          <name>Path-Only URIs</name>
          <t>For absolute paths (URIs starting with ‘/’ but without a scheme), the
leading ‘/’ is treated as the first component:</t>
          <artwork><![CDATA[
Input:  "/a/b/c"

Components:

- Scheme: "" (empty)
- Component 1: "/"
- Component 2: "a/"
- Component 3: "b/"
- Component 4: "c"
]]></artwork>
          <t>For a path with query parameters:</t>
          <artwork><![CDATA[
Input:  "/path/to/file?param=value"

Components:

- Scheme: "" (empty)
- Component 1: "/"
- Component 2: "path/"
- Component 3: "to/"
- Component 4: "file?"
- Component 5: "param=value"
]]></artwork>
          <t>The leading ‘/’ is explicitly encrypted as a component to maintain
consistency and enable proper prefix preservation for absolute paths.</t>
          <t>This character receives its own SIV and is encrypted, ensuring that the
root path is authenticated like any other path component and that
different keys and contexts produce different ciphertexts for that path,
consistently with other paths.</t>
          <t>In applications where all paths are guaranteed to be absolute and the <tt>'/'</tt> path
can be considered a special case, ciphertext expansion can be reduced by
removing the leading <tt>'/'</tt> character from the URI prior to encryption,
treating the path as relative with <tt>'/'</tt> as implicit.</t>
        </section>
      </section>
      <section anchor="component-reconstruction">
        <name>Component Reconstruction</name>
        <t>During decryption, components are joined to reconstruct the original
path:</t>
        <artwork><![CDATA[
Components: ["example.com/", "a/", "b/", "c"]
Reconstructed Path: "example.com/a/b/c"

When combined with the scheme: "https://example.com/a/b/c"
]]></artwork>
        <t>For absolute paths without a scheme:</t>
        <artwork><![CDATA[
Components: ["/", "a/", "b/", "c"]
Reconstructed Path: "/a/b/c"
]]></artwork>
      </section>
    </section>
    <section anchor="cryptographic-operations">
      <name>Cryptographic Operations</name>
      <t>The chained encryption model creates cryptographic dependencies between components, ensuring prefix preservation.</t>
      <artwork><![CDATA[
  URI: "https://example.com/path/to/resource"

  +-------------------+
  |   Component 1:    |
  |  "example.com/"   |
  +-------------------+
            |
            | Plaintext absorbed into components_xof
            v
  +-------------------+
  | SIV1 generation   |------> SIV1 (SIVLEN bytes)
  +-------------------+         |
                                |
                                v
                      Encrypt("example.com/")
                                |
                                v
                      Output1 = SIV1 || Ciphertext1
            |
            | State carries forward
            v
  +-------------------+
  |   Component 2:    |
  |     "path/"       |
  +-------------------+
            |
            | Plaintext absorbed (includes Component 1 state)
            v
  +-------------------+
  | SIV2 generation   |------> SIV2 (depends on 1)
  +-------------------+         |
                                |
                                v
                      Encrypt("path/")
                                |
                                v
                      Output2 = SIV2 || Ciphertext2
            |
            | State carries forward
            v
  +-------------------+
  |   Component 3:    |
  |      "to/"        |
  +-------------------+
            |
            | Plaintext absorbed (includes 1 + 2 state)
            v
  +-------------------+
  | SIV3 generation   |------> SIV3 (depends on 1, 2)
  +-------------------+         |
                                |
                                v
                      Encrypt("to/")
                                |
                                v
                      Output3 = SIV3 || Ciphertext3
            |
            | State carries forward
            v
  +-------------------+
  |   Component 4:    |
  |    "resource"     |
  +-------------------+
            |
            | Plaintext absorbed (includes 1 + 2 + 3 state)
            v
  +-------------------+
  | SIV4 generation   |------> SIV4 (depends on 1, 2, 3)
  +-------------------+         |
                                |
                                v
                      Encrypt("resource")
                                |
                                v
                      Output4 = SIV4 || Ciphertext4

  Final Output: Output1 || Output2 || Output3 || Output4
]]></artwork>
      <t>If URIs share a common prefix <tt>example.com/path/</tt>, their <tt>Output1</tt> and <tt>Output2</tt> will be identical.</t>
      <section anchor="xof-init">
        <name>XOF Initialization</name>
        <t>The base XOF is initialized with the secret key and context
parameters using length-prefixed encoding to prevent ambiguities.</t>
        <t>Two XOF instances are derived from the base XOF:</t>
        <ol spacing="normal" type="1"><li>
            <t>Components XOF: Updated with each component’s plaintext to
generate SIVs</t>
          </li>
          <li>
            <t>Base Keystream XOF: Used as the starting point for generating
keystream for each component</t>
          </li>
        </ol>
        <artwork><![CDATA[
  Input: len(key) || key || len(context) || context

  +-----------------------------------------------------+
  | base_xof = TurboSHAKE128(domain_sep=0x1F)           |
  | base_xof.update(len(secret_key))                    |
  | base_xof.update(secret_key)                         |
  | base_xof.update(len(context))                       |
  | base_xof.update(context)                            |
  +-----------------------------------------------------+
                            |
                            v
               +------------------------+
               |   Clone Base State     |
               +------------------------+
                            |
           +----------------+----------------+
           v                                 v
  +--------------------+          +--------------------+
  |  Components XOF    |          | Base Keystream XOF |
  +--------------------+          +--------------------+
  |   update("IV")     |          |   update("KS")     |
  +--------------------+          +--------------------+
           |                                 |
           |                                 |
           v                                 v
   For SIV Generation              For Keystream Base
   (Updated with each              (Cloned for each
    component plaintext)            component's keystream)
]]></artwork>
        <t>The initialization process is:</t>
        <artwork><![CDATA[
base_xof = TurboSHAKE128()
base_xof.update(len(secret_key))
base_xof.update(secret_key)
base_xof.update(len(context))
base_xof.update(context)

components_xof = base_xof.clone()
components_xof.update("IV")

base_keystream_xof = base_xof.clone()
base_keystream_xof.update("KS")
]]></artwork>
        <t>Note on XOF cloning: The <tt>.clone()</tt> operation creates a new XOF instance with
an identical internal state, preserving all previously absorbed data. After
cloning, the original and cloned XOFs can be updated and read from
independently. This allows the <tt>components_xof</tt> to maintain a running state
across all components while <tt>base_keystream_xof</tt> remains unchanged for creating
per-component keystreams.</t>
      </section>
      <section anchor="component-encryption">
        <name>Component Encryption</name>
        <t>For each component, the encryption process follows a precise sequence
that ensures both confidentiality and authenticity:</t>
        <ol spacing="normal" type="1"><li>
            <t>Update <tt>components_xof</tt> with the component plaintext</t>
          </li>
          <li>
            <t>Squeeze the SIV from <tt>components_xof</tt> (<tt>SIVLEN</tt> bytes). This requires cloning <tt>components_xof</tt> before reading, as reading may finalize the XOF.</t>
          </li>
          <li>
            <t>Create <tt>keystream_xof</tt> by cloning <tt>base_keystream_xof</tt> and updating it with SIV</t>
          </li>
          <li>
            <t>Calculate padding needed for base64 encoding</t>
          </li>
          <li>
            <t>Generate a keystream of length <tt>(component_length + padding)</tt></t>
          </li>
          <li>
            <t>XOR the padded component with the keystream</t>
          </li>
          <li>
            <t>Output SIV concatenated with <tt>encrypted_component</tt></t>
          </li>
        </ol>
        <t>The padding ensures clean base64url encoding without padding characters. Since
base64 encoding works with groups of 3 bytes (producing 4 characters), we pad each
<tt>(SIV || encrypted_component)</tt> pair to have a length that’s a multiple of PADBS:</t>
        <artwork><![CDATA[
total_bytes = SIVLEN (SIV) + component_len
padding_len = (PADBS - total_bytes % PADBS) % PADBS
]]></artwork>
        <t>This formula calculates:</t>
        <ul spacing="normal">
          <li>
            <t>How many bytes are needed to reach the next multiple of PADBS</t>
          </li>
          <li>
            <t>The outer modulo handles the case where <tt>total_bytes</tt> is already a multiple of PADBS</t>
          </li>
        </ul>
        <t>The <tt>components_xof</tt> maintains state across all components.
After generating the SIV for component <tt>N</tt>, the XOF can be updated with component <tt>N+1</tt>’s
plaintext. This chaining ensures that each component’s encryption depends on
all previous components, thus enabling the prefix-preserving property.</t>
      </section>
      <section anchor="component-decryption">
        <name>Component Decryption</name>
        <t>For each encrypted component, the decryption process is:</t>
        <ol spacing="normal" type="1"><li>
            <t>Read SIV from input (<tt>SIVLEN</tt> bytes)</t>
          </li>
          <li>
            <t>Create <tt>keystream_xof</tt> by cloning <tt>base_keystream_xof</tt> and updating it with SIV</t>
          </li>
          <li>
            <t>Decrypt bytes incrementally to determine component boundaries:
            </t>
            <ul spacing="normal">
              <li>
                <t>Generate keystream bytes one at a time from the XOF</t>
              </li>
              <li>
                <t>XOR each encrypted byte with its corresponding keystream byte</t>
              </li>
              <li>
                <t>Check each decrypted byte for component terminators (<tt>'/'</tt>, <tt>'?'</tt>, <tt>'#'</tt>)</t>
              </li>
              <li>
                <t>When a terminator is found, the component is complete.</t>
              </li>
              <li>
                <t>Skip any padding bytes (null bytes) after the component</t>
              </li>
            </ul>
          </li>
          <li>
            <t>Update <tt>components_xof</tt> with the complete plaintext component (including terminator)</t>
          </li>
          <li>
            <t>Generate the expected SIV from <tt>components_xof</tt></t>
          </li>
          <li>
            <t>Compare the expected SIV with the received SIV (constant-time)</t>
          </li>
          <li>
            <t>If mismatch, return <tt>error</tt></t>
          </li>
        </ol>
        <section anchor="component-boundary-detection">
          <name>Component Boundary Detection</name>
          <t>During decryption, component boundaries are discovered dynamically by examining the decrypted plaintext:</t>
          <ul spacing="normal">
            <li>
              <t>Each component (except possibly the last) ends with a terminator character (<tt>'/'</tt>, <tt>'?'</tt>, or <tt>'#'</tt>)</t>
            </li>
            <li>
              <t>When a terminator is encountered, we know the component is complete</t>
            </li>
            <li>
              <t>After finding the terminator, we skip padding bytes to align to the next PADBS-byte boundary.</t>
            </li>
            <li>
              <t>The padding length can be calculated: <tt>padding = (PADBS - ((SIVLEN + bytes_read) % PADBS)) % PADBS</tt></t>
            </li>
          </ul>
          <t>This approach eliminates the need for explicit length encoding, as the component structure itself provides the necessary boundary information.</t>
          <t>Any tampering with the encrypted data will cause the SIV comparison to fail.</t>
        </section>
      </section>
      <section anchor="padding-and-encoding">
        <name>Padding and Encoding</name>
        <t>To enable clean base64url encoding without padding characters (‘=’), each
encrypted component pair <tt>(SIV || ciphertext)</tt> is padded to be a multiple of PADBS bytes.
This is necessary because base64 encoding processes 3 bytes at a time to produce
4 characters of output.</t>
        <t>The padding calculation <tt>(PADBS - (SIVLEN + component_len) % PADBS) % PADBS</tt> ensures the following:</t>
        <ul spacing="normal">
          <li>
            <t>If <tt>(SIVLEN + component_len) % PADBS = 0</tt>: no padding needed (already aligned)</t>
          </li>
          <li>
            <t>If <tt>(SIVLEN + component_len) % PADBS = 1</tt>: add 2 bytes of padding</t>
          </li>
          <li>
            <t>If <tt>(SIVLEN + component_len) % PADBS = 2</tt>: add 1 byte of padding</t>
          </li>
        </ul>
        <t>With the default value of <tt>PADBS=3</tt>, this padding scheme provides partial length-hiding.
For example, with <tt>SIVLEN=16</tt>, components “abc”, “abcd”, and “abcde” all produce 21-byte
outputs after padding. Without the secret key, a passive adversary cannot determine
the exact original component size.</t>
        <t>The final output is encoded using URL-safe base64 <xref target="RFC4648"/>, with ‘-‘ replacing
‘+’ and ‘_’ replacing ‘/’ for URI compatibility.</t>
      </section>
    </section>
    <section anchor="algorithm-specification">
      <name>Algorithm Specification</name>
      <t>This section provides the complete algorithms for encryption and
decryption. The following functions and operations are used throughout
the algorithms:</t>
      <ul spacing="normal">
        <li>
          <t><tt>TurboSHAKE128()</tt>: Creates a new TurboSHAKE128 XOF instance with domain separation parameter 0x1F. This function produces an extensible output function (XOF) that can generate arbitrary-length outputs.</t>
        </li>
        <li>
          <t><tt>.update(data)</tt>: Absorbs the provided data into the XOF state. Data is processed sequentially and updates the internal state of the XOF.</t>
        </li>
        <li>
          <t><tt>.read(length)</tt>: Squeezes the specified number of bytes from the XOF’s output. Each call continues from where the previous read left off, producing a continuous stream of pseudorandom bytes.</t>
        </li>
        <li>
          <t><tt>.clone()</tt>: Creates a new XOF instance with an identical internal state to the original. This enables multiple independent computation paths from the same initial state.</t>
        </li>
        <li>
          <t>XOR operation: The bitwise exclusive OR operation between two byte sequences of equal length. This operation is used to combine plaintext with keystream for encryption, and ciphertext with keystream for decryption.</t>
        </li>
        <li>
          <t><tt>base64url_encode(data)</tt>: Converts binary data to a base64 string using URL-safe encoding (replacing ‘+’ with ‘-‘ and ‘/’ with ‘_’) and omitting padding characters.</t>
        </li>
        <li>
          <t><tt>base64url_decode(string)</tt>: Converts a URL-safe base64 string back to binary data, automatically handling the absence of padding characters.</t>
        </li>
        <li>
          <t><tt>Stream(data)</tt>: Creates a sequential reader for binary data, enabling byte-by-byte or block-based access to the contents.</t>
        </li>
        <li>
          <t><tt>constant_time_compare(a, b)</tt>: Compares two byte sequences in constant time, regardless of their contents. This prevents timing attacks by ensuring the comparison duration does not depend on which bytes differ.</t>
        </li>
        <li>
          <t><tt>len(data)</tt>: Returns the length of the provided data in bytes.</t>
        </li>
        <li>
          <t>Concatenation: The operation of joining two byte sequences end-to-end to form a single sequence.</t>
        </li>
        <li>
          <t><tt>zeros(count)</tt>: Generates a sequence of zero-valued bytes of the specified length, used for padding.</t>
        </li>
        <li>
          <t><tt>remove_padding(data)</tt>: Removes trailing zero bytes from a byte sequence to recover the original data length.</t>
        </li>
        <li>
          <t><tt>join(components)</tt>: Combines multiple path components into a single path string, preserving the terminator characters (<tt>'/'</tt>, <tt>'?'</tt>, <tt>'#'</tt>) that are included in each component.</t>
        </li>
      </ul>
      <section anchor="encryption-algorithm">
        <name>Encryption Algorithm</name>
        <t>Input: secret_key, context, uri_string</t>
        <t>Output: encrypted_uri</t>
        <t>Steps:</t>
        <ol spacing="normal" type="1"><li>
            <t>Split URI into scheme and components</t>
          </li>
          <li>
            <t>Initialize XOF instances as described in <xref target="xof-init"/></t>
          </li>
          <li>
            <t><tt>encrypted_output = empty byte array</tt></t>
          </li>
          <li>
            <t>For each component:
            </t>
            <ul spacing="normal">
              <li>
                <t>Update <tt>components_xof</tt> with <tt>component</tt></t>
              </li>
              <li>
                <t><tt>SIV = components_xof.clone().read(SIVLEN)</tt></t>
              </li>
              <li>
                <t><tt>keystream_xof = base_keystream_xof.clone()</tt></t>
              </li>
              <li>
                <t><tt>keystream_xof.update(SIV)</tt></t>
              </li>
              <li>
                <t><tt>padding_len = (PADBS - (SIVLEN + len(component)) % PADBS) % PADBS</tt></t>
              </li>
              <li>
                <t><tt>keystream = keystream_xof.read(len(component) + padding_len)</tt></t>
              </li>
              <li>
                <t><tt>padded_component = component concatenated with zeros(padding_len)</tt></t>
              </li>
              <li>
                <t><tt>encrypted_part = padded_component XOR keystream</tt></t>
              </li>
              <li>
                <t><tt>encrypted_output = encrypted_output concatenated with SIV concatenated with encrypted_part</tt></t>
              </li>
            </ul>
          </li>
          <li>
            <t><tt>base64_output = base64url_encode(encrypted_output)</tt></t>
          </li>
          <li>
            <t>If scheme is not empty: Return <tt>scheme + base64_output</tt></t>
          </li>
          <li>
            <t>Else if original URI started with ‘/’: Return <tt>'/' + base64_output</tt></t>
          </li>
          <li>
            <t>Else: Return <tt>base64_output</tt></t>
          </li>
        </ol>
      </section>
      <section anchor="decryption-algorithm">
        <name>Decryption Algorithm</name>
        <t>Input: secret_key, context, encrypted_uri</t>
        <t>Output: decrypted_uri or error</t>
        <t>Note: For path-only URIs (those starting with ‘/’), the output format is:
- ‘/’ followed by the base64url-encoded encrypted components
- This preserves the absolute path indicator in the encrypted form</t>
        <t>Steps:</t>
        <ol spacing="normal" type="1"><li>
            <t>Split encrypted URI into scheme and base64 part</t>
          </li>
          <li>
            <t><tt>decoded = base64url_decode(base64_part)</tt> If decoding fails, return <tt>error</tt></t>
          </li>
          <li>
            <t>Initialize XOF instances as described in <xref target="xof-init"/></t>
          </li>
          <li>
            <t><tt>decrypted_components = empty list</tt></t>
          </li>
          <li>
            <t><tt>position = 0</tt></t>
          </li>
          <li>
            <t>While <tt>position &lt; len(decoded)</tt>:
            </t>
            <ul spacing="normal">
              <li>
                <t><tt>SIV = decoded[position:position+SIVLEN]</tt> If not enough bytes, return <tt>error</tt></t>
              </li>
              <li>
                <t><tt>keystream_xof = base_keystream_xof.clone()</tt></t>
              </li>
              <li>
                <t><tt>keystream_xof.update(SIV)</tt></t>
              </li>
              <li>
                <t><tt>component_start = position + SIVLEN</tt></t>
              </li>
              <li>
                <t><tt>component = empty byte array</tt></t>
              </li>
              <li>
                <t><tt>position = position + SIVLEN</tt></t>
              </li>
              <li>
                <t>While <tt>position &lt; len(decoded)</tt>:
                </t>
                <ul spacing="normal">
                  <li>
                    <t><tt>decrypted_byte = decoded[position] XOR keystream_xof.read(1)</tt></t>
                  </li>
                  <li>
                    <t><tt>position = position + 1</tt></t>
                  </li>
                  <li>
                    <t>If <tt>decrypted_byte == 0x00</tt>: continue (skip padding)</t>
                  </li>
                  <li>
                    <t><tt>component.append(decrypted_byte)</tt></t>
                  </li>
                  <li>
                    <t>If <tt>decrypted_byte</tt> is <tt>'/'</tt>, <tt>'?'</tt>, or <tt>'#'</tt>:
                    </t>
                    <ul spacing="normal">
                      <li>
                        <t><tt>total_len = position - component_start</tt></t>
                      </li>
                      <li>
                        <t><tt>position = position + ((PADBS - ((SIVLEN + total_len) % PADBS)) % PADBS)</tt></t>
                      </li>
                      <li>
                        <t>Break inner loop</t>
                      </li>
                    </ul>
                  </li>
                </ul>
              </li>
              <li>
                <t>Update <tt>components_xof</tt> with <tt>component</tt></t>
              </li>
              <li>
                <t><tt>expected_SIV = components_xof.clone().read(SIVLEN)</tt></t>
              </li>
              <li>
                <t>If <tt>constant_time_compare(SIV, expected_SIV) == false</tt>, return <tt>error</tt></t>
              </li>
              <li>
                <t><tt>decrypted_components.append(component)</tt></t>
              </li>
            </ul>
          </li>
          <li>
            <t><tt>decrypted_path = join(decrypted_components)</tt></t>
          </li>
          <li>
            <t>Return <tt>scheme + decrypted_path</tt></t>
          </li>
        </ol>
      </section>
    </section>
    <section anchor="implementation-details">
      <name>Implementation Details</name>
      <section anchor="turboshake128-usage">
        <name>TurboSHAKE128 Usage</name>
        <t>Implementations MUST use TurboSHAKE128 with a domain separation
parameter of <tt>0x1F</tt> for all operations. The TurboSHAKE128 XOF is used
for:</t>
        <ul spacing="normal">
          <li>
            <t>Generating SIVs from the components XOF</t>
          </li>
          <li>
            <t>Generating keystream for encryption/decryption</t>
          </li>
          <li>
            <t>All XOF operations in the initialization</t>
          </li>
        </ul>
        <t>TurboSHAKE128 is specified in <xref target="RFC9861"/> and provides the security
properties needed for this construction.</t>
      </section>
      <section anchor="key-and-context-handling">
        <name>Key and Context Handling</name>
        <t>The secret key MUST be at least <tt>SIVLEN</tt> bytes long. Keys shorter than <tt>SIVLEN</tt>
bytes MUST be rejected. Implementations SHOULD validate that the key
does not consist of repeated patterns (e.g., identical first and
second halves) as a best practice.</t>
        <t>The context parameter is a string that provides domain separation.
Different applications SHOULD use different context strings to prevent
cross-application attacks. The context string MAY be empty.</t>
        <t>Both key and context are length-prefixed when absorbed into the base
XOF:</t>
        <artwork><![CDATA[
base_xof.update(len(secret_key) as uint8)
base_xof.update(secret_key)
base_xof.update(len(context) as uint8)
base_xof.update(context)
]]></artwork>
        <t>The length is encoded as a single byte, limiting keys and contexts to
255 bytes. This is sufficient for all practical use cases.</t>
      </section>
      <section anchor="error-handling">
        <name>Error Handling</name>
        <t>Implementations MUST NOT reveal the cause of decryption failures. All
error conditions (invalid base64, incorrect padding, SIV mismatch,
insufficient data) MUST result in identical, generic error messages.</t>
        <t>SIV comparison MUST be performed in constant-time to prevent timing
attacks.</t>
      </section>
    </section>
    <section anchor="security-guarantees">
      <name>Security Guarantees</name>
      <t>URICrypt provides the following cryptographic security guarantees:</t>
      <section anchor="confidentiality">
        <name>Confidentiality</name>
        <t>URICrypt achieves semantic security for URI path components through its use of TurboSHAKE128 as a pseudorandom function. Each component is encrypted using a unique keystream derived from the following:</t>
        <ul spacing="normal">
          <li>
            <t>The secret key</t>
          </li>
          <li>
            <t>The application context</t>
          </li>
          <li>
            <t>A synthetic initialization vector (SIV) that depends on all preceding components</t>
          </li>
        </ul>
        <t>This construction ensures that:</t>
        <ul spacing="normal">
          <li>
            <t>An attacker without the secret key cannot recover plaintext components from ciphertexts.</t>
          </li>
          <li>
            <t>The keystream generation is computationally indistinguishable from random for each unique (key, context, path-prefix) tuple.</t>
          </li>
          <li>
            <t>Components are protected by at least 128 bits of security against brute-force attacks.</t>
          </li>
        </ul>
      </section>
      <section anchor="authenticity-and-integrity">
        <name>Authenticity and Integrity</name>
        <t>Each URI component is authenticated through the SIV mechanism:</t>
        <ul spacing="normal">
          <li>
            <t>The SIV acts as a Message Authentication Code (MAC) computed over the component and all preceding components.</t>
          </li>
          <li>
            <t>Any modification to a component will cause the SIV verification to fail during decryption.</t>
          </li>
          <li>
            <t>The chained construction ensures that reordering, insertion, or deletion of components is detected.</t>
          </li>
          <li>
            <t>Authentication provides 128-bit security against forgery attempts.</t>
          </li>
        </ul>
      </section>
      <section anchor="prefix-preserving-property">
        <name>Prefix-Preserving Property</name>
        <t>URICrypt maintains a controlled information leakage pattern:</t>
        <ul spacing="normal">
          <li>
            <t>URIs sharing a common prefix will produce ciphertexts with the same encrypted prefix.</t>
          </li>
          <li>
            <t>This property is deterministic and intentional, enabling systems to perform prefix-based operations.</t>
          </li>
          <li>
            <t>The leakage is limited to prefix structure only—no information about non-matching suffixes is revealed.</t>
          </li>
        </ul>
      </section>
      <section anchor="domain-separation">
        <name>Domain Separation</name>
        <t>The context parameter provides cryptographic domain separation:</t>
        <ul spacing="normal">
          <li>
            <t>Different contexts with the same key produce completely independent ciphertexts.</t>
          </li>
          <li>
            <t>This prevents cross-context attacks where ciphertexts from one application could be used in another.</t>
          </li>
          <li>
            <t>Context binding is cryptographically enforced through the XOF initialization.</t>
          </li>
        </ul>
      </section>
      <section anchor="key-commitment">
        <name>Key Commitment</name>
        <t>URICrypt provides full key-commitment security.</t>
        <t>The scheme is fully key-committing, meaning that a ciphertext can only be decrypted with the exact key that was used to encrypt it. It is computationally infeasible to find two different keys that successfully decrypt the same ciphertext to valid plaintexts.</t>
      </section>
      <section anchor="resistance-to-common-attacks">
        <name>Resistance to Common Attacks</name>
        <t>URICrypt resists several categories of attacks:</t>
        <t>Chosen-plaintext Attacks (CPA): While deterministic, URICrypt is CPA-secure for unique inputs. The determinism is a design requirement for prefix preservation.</t>
        <t>Tampering Detection: Any bit flip, truncation, or modification in the ciphertext will be detected with overwhelming probability (1 - 2<sup>-128</sup>).</t>
        <t>Length-extension Attacks: The use of length-prefixed encoding and domain separation prevents length-extension attacks.</t>
        <t>Replay Attacks: Within a single (key, context) pair, replay is possible due to determinism. Applications requiring replay protection should incorporate timestamps or nonces into the context.</t>
        <t>Key Recovery: TurboSHAKE128’s security properties ensure that observing ciphertexts does not leak information about the secret key.</t>
      </section>
      <section anchor="security-bounds">
        <name>Security Bounds</name>
        <t>The security of URICrypt is bounded by the following:</t>
        <ul spacing="normal">
          <li>
            <t>Key strength: Minimum 128-bit security with <tt>SIVLEN</tt>-byte keys</t>
          </li>
          <li>
            <t>Collision resistance: 2<sup>64</sup> birthday bound for SIV collisions</t>
          </li>
          <li>
            <t>Authentication security: 2<sup>-128</sup> probability of successful forgery</t>
          </li>
          <li>
            <t>Computational security: Based on TurboSHAKE128’s proven security as an XOF</t>
          </li>
        </ul>
      </section>
      <section anchor="limitations-and-trade-offs">
        <name>Limitations and Trade-offs</name>
        <t>URICrypt makes specific security trade-offs for functionality, including the following:</t>
        <ul spacing="normal">
          <li>
            <t>Deterministic encryption: Same inputs produce same outputs, enabling certain traffic analysis</t>
          </li>
          <li>
            <t>Partial length obfuscation: With <tt>PADBS=3</tt>, exact component lengths are partially hidden</t>
          </li>
          <li>
            <t>Prefix structure leakage: The hierarchical structure of URIs is preserved by design</t>
          </li>
          <li>
            <t>SIV length configuration: Implementations MAY adjust <tt>SIVLEN</tt> for different usage bounds. Larger values (24 or 32 bytes) increase birthday bound resistance at the cost of ciphertext expansion. However, 16 bytes is generally recommended as it provides practical collision resistance with acceptable overhead</t>
          </li>
          <li>
            <t>Padding block size configuration: The default <tt>PADBS=3</tt> already provides partial length-hiding. Implementations MAY adjust <tt>PADBS</tt> to increase size obfuscation. Larger values create larger size buckets but increase ciphertext expansion. The value MUST remain a multiple of 3 to ensure efficient Base64url encoding without padding characters</t>
          </li>
        </ul>
        <t>These trade-offs are intentional and necessary for the prefix-preserving functionality. Applications requiring stronger privacy guarantees should evaluate whether URICrypt’s properties align with their threat model.</t>
      </section>
    </section>
    <section anchor="security-considerations">
      <name>Security Considerations</name>
      <t>URICrypt provides confidentiality and integrity for URI paths while
preserving prefix relationships. The encryption is fully reversible:
encrypted URIs can be decrypted to recover the original plaintext URIs,
but only with knowledge of the secret key. The security properties depend on:</t>
      <ul spacing="normal">
        <li>
          <t>Key Secrecy: The security of URICrypt depends entirely on the
secrecy of the secret key. Keys MUST be generated using a
cryptographically secure random number generator <xref target="RFC4086"/> and
stored securely.</t>
        </li>
        <li>
          <t>Deterministic Encryption: URICrypt is deterministic - identical
inputs produce identical outputs. This allows observers to detect
when the same URI is encrypted multiple times. Applications
requiring unlinkability SHOULD incorporate additional entropy (e.g.,
via the context parameter).</t>
        </li>
        <li>
          <t>Prefix Preservation: While essential for functionality, prefix
preservation leaks information about URI structure. Systems where
this information is sensitive SHOULD consider alternative
approaches.</t>
        </li>
        <li>
          <t>Context Separation: The context parameter prevents cross-context
attacks. Applications MUST use distinct contexts for different
purposes, even when sharing keys.</t>
        </li>
        <li>
          <t>Component Authentication: Each component is authenticated via
the SIV mechanism. Any modification, reordering, or truncation of
components will be detected during decryption.</t>
        </li>
        <li>
          <t>Length Obfuscation: The default <tt>PADBS=3</tt> configuration provides partial
length-hiding. Applications requiring stronger length-hiding
SHOULD consider using larger <tt>PADBS</tt> values or padding components to fixed lengths.</t>
        </li>
        <li>
          <t>Key Reuse: Using the same key with different contexts is safe, but
using the same (key, context) pair for different applications is
NOT RECOMMENDED.</t>
        </li>
      </ul>
    </section>
    <section numbered="false" anchor="iana-considerations">
      <name>IANA Considerations</name>
      <t>This document has no actions for IANA.</t>
    </section>
    <section numbered="false" anchor="acknowledgments">
      <name>Acknowledgments</name>
      <t>The author would like to thank Maciej Soltysiak for highlighting the importance of properly supporting query parameters and fragments in URI encryption.</t>
    </section>
  </middle>
  <back>
    <references anchor="sec-normative-references">
      <name>Normative References</name>
      <reference anchor="I-D.draft-denis-ipcrypt">
        <front>
          <title>Methods for IP Address Encryption and Obfuscation</title>
          <author fullname="Frank Denis" initials="F." surname="Denis">
            <organization>Fastly Inc.</organization>
          </author>
          <date day="19" month="September" year="2025"/>
          <abstract>
            <t>   This document specifies secure, efficient methods for encrypting IP
   addresses for privacy-preserving storage, logging, and analytics.
   Unlike truncation, which destroys data irreversibly, these methods
   are reversible with the encryption key while providing strong privacy
   guarantees.

   Four modes are defined: ipcrypt-deterministic (format-preserving,
   16-byte output), ipcrypt-pfx (prefix-preserving, native address
   size), ipcrypt-nd and ipcrypt-ndx (non-deterministic with random
   tweaks).  All support high-performance processing at network speeds
   and produce interoperable results across implementations.

            </t>
          </abstract>
        </front>
        <seriesInfo name="Internet-Draft" value="draft-denis-ipcrypt-12"/>
      </reference>
      <reference anchor="RFC2119">
        <front>
          <title>Key words for use in RFCs to Indicate Requirement Levels</title>
          <author fullname="S. Bradner" initials="S." surname="Bradner"/>
          <date month="March" year="1997"/>
          <abstract>
            <t>In many standards track documents several words are used to signify the requirements in the specification. These words are often capitalized. This document defines these words as they should be interpreted in IETF documents. This document specifies an Internet Best Current Practices for the Internet Community, and requests discussion and suggestions for improvements.</t>
          </abstract>
        </front>
        <seriesInfo name="BCP" value="14"/>
        <seriesInfo name="RFC" value="2119"/>
        <seriesInfo name="DOI" value="10.17487/RFC2119"/>
      </reference>
      <reference anchor="RFC8174">
        <front>
          <title>Ambiguity of Uppercase vs Lowercase in RFC 2119 Key Words</title>
          <author fullname="B. Leiba" initials="B." surname="Leiba"/>
          <date month="May" year="2017"/>
          <abstract>
            <t>RFC 2119 specifies common key words that may be used in protocol specifications. This document aims to reduce the ambiguity by clarifying that only UPPERCASE usage of the key words have the defined special meanings.</t>
          </abstract>
        </front>
        <seriesInfo name="BCP" value="14"/>
        <seriesInfo name="RFC" value="8174"/>
        <seriesInfo name="DOI" value="10.17487/RFC8174"/>
      </reference>
      <reference anchor="RFC3986">
        <front>
          <title>Uniform Resource Identifier (URI): Generic Syntax</title>
          <author fullname="T. Berners-Lee" initials="T." surname="Berners-Lee"/>
          <author fullname="R. Fielding" initials="R." surname="Fielding"/>
          <author fullname="L. Masinter" initials="L." surname="Masinter"/>
          <date month="January" year="2005"/>
          <abstract>
            <t>A Uniform Resource Identifier (URI) is a compact sequence of characters that identifies an abstract or physical resource. This specification defines the generic URI syntax and a process for resolving URI references that might be in relative form, along with guidelines and security considerations for the use of URIs on the Internet. The URI syntax defines a grammar that is a superset of all valid URIs, allowing an implementation to parse the common components of a URI reference without knowing the scheme-specific requirements of every possible identifier. This specification does not define a generative grammar for URIs; that task is performed by the individual specifications of each URI scheme. [STANDARDS-TRACK]</t>
          </abstract>
        </front>
        <seriesInfo name="STD" value="66"/>
        <seriesInfo name="RFC" value="3986"/>
        <seriesInfo name="DOI" value="10.17487/RFC3986"/>
      </reference>
      <reference anchor="RFC4648">
        <front>
          <title>The Base16, Base32, and Base64 Data Encodings</title>
          <author fullname="S. Josefsson" initials="S." surname="Josefsson"/>
          <date month="October" year="2006"/>
          <abstract>
            <t>This document describes the commonly used base 64, base 32, and base 16 encoding schemes. It also discusses the use of line-feeds in encoded data, use of padding in encoded data, use of non-alphabet characters in encoded data, use of different encoding alphabets, and canonical encodings. [STANDARDS-TRACK]</t>
          </abstract>
        </front>
        <seriesInfo name="RFC" value="4648"/>
        <seriesInfo name="DOI" value="10.17487/RFC4648"/>
      </reference>
      <reference anchor="RFC9861">
        <front>
          <title>KangarooTwelve and TurboSHAKE</title>
          <author fullname="B. Viguier" initials="B." surname="Viguier"/>
          <author fullname="D. Wong" initials="D." role="editor" surname="Wong"/>
          <author fullname="G. Van Assche" initials="G." role="editor" surname="Van Assche"/>
          <author fullname="Q. Dang" initials="Q." role="editor" surname="Dang"/>
          <author fullname="J. Daemen" initials="J." role="editor" surname="Daemen"/>
          <date month="October" year="2025"/>
          <abstract>
            <t>This document defines four eXtendable-Output Functions (XOFs), hash functions with output of arbitrary length, named TurboSHAKE128, TurboSHAKE256, KT128, and KT256.</t>
            <t>All four functions provide efficient and secure hashing primitives, and the last two are able to exploit the parallelism of the implementation in a scalable way.</t>
            <t>This document is a product of the Crypto Forum Research Group. It builds up on the definitions of the permutations and of the sponge construction in NIST FIPS 202 and is meant to serve as a stable reference and an implementation guide.</t>
          </abstract>
        </front>
        <seriesInfo name="RFC" value="9861"/>
        <seriesInfo name="DOI" value="10.17487/RFC9861"/>
      </reference>
      <reference anchor="RFC4086">
        <front>
          <title>Randomness Requirements for Security</title>
          <author fullname="D. Eastlake 3rd" initials="D." surname="Eastlake 3rd"/>
          <author fullname="J. Schiller" initials="J." surname="Schiller"/>
          <author fullname="S. Crocker" initials="S." surname="Crocker"/>
          <date month="June" year="2005"/>
          <abstract>
            <t>Security systems are built on strong cryptographic algorithms that foil pattern analysis attempts. However, the security of these systems is dependent on generating secret quantities for passwords, cryptographic keys, and similar quantities. The use of pseudo-random processes to generate secret quantities can result in pseudo-security. A sophisticated attacker may find it easier to reproduce the environment that produced the secret quantities and to search the resulting small set of possibilities than to locate the quantities in the whole of the potential number space.</t>
            <t>Choosing random quantities to foil a resourceful and motivated adversary is surprisingly difficult. This document points out many pitfalls in using poor entropy sources or traditional pseudo-random number generation techniques for generating such quantities. It recommends the use of truly random hardware techniques and shows that the existing hardware on many systems can be used for this purpose. It provides suggestions to ameliorate the problem when a hardware solution is not available, and it gives examples of how large such quantities need to be for some applications. This document specifies an Internet Best Current Practices for the Internet Community, and requests discussion and suggestions for improvements.</t>
          </abstract>
        </front>
        <seriesInfo name="BCP" value="106"/>
        <seriesInfo name="RFC" value="4086"/>
        <seriesInfo name="DOI" value="10.17487/RFC4086"/>
      </reference>
    </references>
    <?line 811?>

<section anchor="pseudocode">
      <name>Pseudocode</name>
      <section anchor="uri-component-extraction-1">
        <name>URI Component Extraction</name>
        <artwork><![CDATA[
function extract_components(uri_string):
  if uri_string contains "://":
     scheme = substring up to and including "://"
     path = substring after "://"
  else:
     scheme = ""
     path = uri_string

  components = []

  // For absolute paths, treat leading "/" as first component
  if path starts with "/":
     components.append("/")
     path = substring after first "/"

  while path is not empty:
     terminator_pos = find_next_terminator(path)
     if terminator_pos found:
        component = substring(path, 0, terminator_pos + 1)
        path = substring(path, terminator_pos + 1)
        components.append(component)
     else:
        components.append(path)
        path = ""

  return (scheme, components)

function find_next_terminator(path):
  for i from 0 to length(path) - 1:
     if path[i] == '/' or path[i] == '?' or path[i] == '#':
        return i
  return not_found
]]></artwork>
      </section>
      <section anchor="xof-initialization">
        <name>XOF Initialization</name>
        <artwork><![CDATA[
function initialize_xofs(secret_key, context):
  // Initialize base XOF
  base_xof = TurboSHAKE128(0x1F)

  // Absorb key and context with length prefixes
  base_xof.update(uint8(len(secret_key)))
  base_xof.update(secret_key)
  base_xof.update(uint8(len(context)))
  base_xof.update(context)

  // Create components XOF
  components_xof = base_xof.clone()
  components_xof.update("IV")

  // Create base keystream XOF
  base_keystream_xof = base_xof.clone()
  base_keystream_xof.update("KS")

  return (components_xof, base_keystream_xof)
]]></artwork>
      </section>
      <section anchor="encryption-algorithm-1">
        <name>Encryption Algorithm</name>
        <artwork><![CDATA[
function uricrypt_encrypt(secret_key, context, uri_string):
  // Extract components
  (scheme, components) = extract_components(uri_string)

  // Initialize XOF instances with secret key and context
  (components_xof, base_keystream_xof) =
      initialize_xofs(secret_key, context)
  if error: return error

  encrypted_output = byte_array()

  // Process each component
  for component in components:
     // Update components XOF for SIV computation
     components_xof.update(component)

     // Generate SIVLEN-byte Synthetic Initialization Vector (SIV)
     siv = components_xof.clone().squeeze(SIVLEN)

     // Create keystream XOF for this component
     keystream_xof = base_keystream_xof.clone()
     keystream_xof.update(siv)

     // Calculate padding for base64 encoding alignment
     // The total bytes (SIV + component) must be a multiple of PADBS
     // to produce clean base64 output without padding characters
     component_len = len(component)
     padding_len = (PADBS - (SIVLEN + component_len) % PADBS) % PADBS

     // Generate keystream
     keystream = keystream_xof.squeeze(component_len + padding_len)

     // Pad component to align with base64 encoding requirements
     padded_component = component + byte_array(padding_len)

     // Encrypt using XOR with keystream
     encrypted_part = xor_bytes(padded_component, keystream)

     // Append to output
     encrypted_output.extend(siv)
     encrypted_output.extend(encrypted_part)

  // Base64 encode with URL-safe characters and no padding
  base64_output = base64_urlsafe_no_pad_encode(encrypted_output)

  // Return with appropriate prefix
  if scheme != "":
     return scheme + base64_output
  else if uri_string starts with "/":
     return "/" + base64_output
  else:
     return base64_output
]]></artwork>
      </section>
      <section anchor="decryption-algorithm-1">
        <name>Decryption Algorithm</name>
        <artwork><![CDATA[
function uricrypt_decrypt(secret_key, context, encrypted_uri):
  // Split scheme and base64
  if encrypted_uri contains "://":
     scheme = substring up to and including "://"
     base64_part = substring after "://"
  else if encrypted_uri starts with "/":
     // Path-only URI: strip leading "/" before decoding
     scheme = ""
     base64_part = substring after first "/"
  else:
     scheme = ""
     base64_part = encrypted_uri

  // Decode base64
  try:
     decoded = base64_urlsafe_no_pad_decode(base64_part)
  catch:
     return error("Decryption failed")

  // Initialize XOF instances with secret key and context
  (components_xof, base_keystream_xof) =
      initialize_xofs(secret_key, context)
  if error: return error

  decrypted_components = []
  input_stream = ByteStream(decoded)

  // Process each component
  while not input_stream.empty():
     // Read SIV
     siv = input_stream.read(SIVLEN)
     if len(siv) != SIVLEN:
        return error("Decryption failed")

     // Create keystream XOF
     keystream_xof = base_keystream_xof.clone()
     keystream_xof.update(siv)

     // Decrypt byte-by-byte to find component boundary
     component = byte_array()
     component_start = input_stream.position()

     while not input_stream.empty():
        // Decrypt one byte
        encrypted_byte = input_stream.read(1)
        if len(encrypted_byte) != 1:
           return error("Decryption failed")

        keystream_byte = keystream_xof.squeeze(1)
        decrypted_byte = xor_bytes(encrypted_byte, keystream_byte)[0]

        // Skip padding (null bytes)
        if decrypted_byte == 0x00:
           continue

        // Add to component
        component.append(decrypted_byte)

        // Check for terminator
        if decrypted_byte == '/' or decrypted_byte == '?' or decrypted_byte == '#':
           // Component complete - skip remaining padding
           total_len = input_stream.position() - component_start
           padding_len = (PADBS - ((SIVLEN + total_len) % PADBS)) % PADBS
           input_stream.skip(padding_len)
           break

     // Update XOF with plaintext
     components_xof.update(component)

     // Generate expected SIV
     expected_siv = components_xof.clone().squeeze(SIVLEN)

     // Authenticate using constant-time comparison to prevent timing attacks
     if not constant_time_equal(siv, expected_siv):
        return error("Decryption failed")

     decrypted_components.append(component)

  // Reconstruct URI
  path = "".join(decrypted_components)
  return scheme + path
]]></artwork>
      </section>
      <section anchor="padding-and-encoding-1">
        <name>Padding and Encoding</name>
        <artwork><![CDATA[
function calculate_padding(component_len):
  // Calculate padding needed for base64 encoding alignment
  // The combined SIV (SIVLEN bytes) + component must be divisible by PADBS
  // for clean base64 encoding without '=' padding characters
  total_len = SIVLEN + component_len
  return (PADBS - total_len % PADBS) % PADBS

function base64_urlsafe_no_pad_encode(data):
  // Use standard base64 encoding
  encoded = standard_base64_encode(data)
  // Make URL-safe and remove padding for URI compatibility
  encoded = encoded.replace('+', '-')
                   .replace('/', '_')
                   .rstrip('=')
  return encoded

function base64_urlsafe_no_pad_decode(encoded):
  // Add padding if needed for standard decoder
  padding = (4 - len(encoded) % 4) % 4
  if padding > 0:
     encoded = encoded + ('=' * padding)
  // Make standard base64
  encoded = encoded.replace('-', '+')
                   .replace('_', '/')
  // Decode
  return standard_base64_decode(encoded)
]]></artwork>
      </section>
    </section>
    <section anchor="test-vectors">
      <name>Test Vectors</name>
      <t>These test vectors were generated using the reference Rust implementation
of URICrypt with TurboSHAKE128.</t>
      <artwork><![CDATA[
Test Configuration:
secret_key (hex): 0102030405060708090a0b0c0d0e0f10
context: "test-context"
]]></artwork>
      <section anchor="test-vector-1-full-uri">
        <name>Test Vector 1: Full URI</name>
        <artwork><![CDATA[
Input: "https://example.com/a/b/c"
Output: "https://HOGo9vauZ3b3xsPNPQng5apSzL5V7QW94C7USgN8mHZJ337AKSWOu
         cUwMuD-uUfF95SsSHCNgBkXUnH1uGll_YtBltXSqKEHNcYJJwbdFdhfWz19"
]]></artwork>
      </section>
      <section anchor="test-vector-2-path-only-uri">
        <name>Test Vector 2: Path-Only URI</name>
        <artwork><![CDATA[
Input: "/a/b/c"
Output: "/b9bCOhqZsvU9XxGOMk6d8QFQhTIdI_xYKpds2lWXpZCms5-az9wtfUft3rec
         3d9YkUo0N7VcxO5MXfxE5UobvgTJX8UpRdNN"
]]></artwork>
      </section>
      <section anchor="test-vector-3-multi-component-path">
        <name>Test Vector 3: Multi-Component Path</name>
        <artwork><![CDATA[
Input: "https://cdn.example.com/videos/2025/03/file.mp4"
Output: "https://hxUM2N3txwYjGxjvCpWn30SznxR0v0fDbkSQgCTXCUu7Rq8iSbWP4
         0OvYxKs9zC3kw1JNzAc4Wuj7RZvRd0VUprJWLs5KJPnWsA9Kguxa_J7XviTS3G
         Tqf-XZdPxYyq1Y1MXVE9_4ojHwm6jBDUkVthAkuNe5Cqk_h6d"
]]></artwork>
      </section>
      <section anchor="test-vector-4-root-with-scheme">
        <name>Test Vector 4: Root with Scheme</name>
        <artwork><![CDATA[
Input: "https://example.com/"
Output: "https://HOGo9vauZ3b3xsPNPQng5apSzL5V7QW94C7USgN8"
]]></artwork>
      </section>
      <section anchor="test-vector-5-simple-path">
        <name>Test Vector 5: Simple Path</name>
        <artwork><![CDATA[
Input: "/path/to/resource"
Output: "/b9bCOhqZsvU9XxGOMk6d8QFQPTuMlsQKDBhAbc77JvsdRj0kxiFipunATQmm
         CkNhAe0BPP2EqQoxORElY_ukfUYSrr9mIMfiO9joa3Kn5RS7eSKr"
]]></artwork>
      </section>
      <section anchor="test-vector-6-uri-with-query-parameters">
        <name>Test Vector 6: URI with Query Parameters</name>
        <artwork><![CDATA[
Input: "https://example.com/search?q=test&limit=10"
Output: "https://HOGo9vauZ3b3xsPNPQng5apSzL5V7QW94C7USgN8cl2BBtuWmxTsI
         Ij59ka3KeDsaqXFGnKgW9aLLR36YvUf9ORkMnVE5PTR_3DiO43hL9WjdSu7L9
         FN"
]]></artwork>
      </section>
      <section anchor="test-vector-7-uri-with-fragment">
        <name>Test Vector 7: URI with Fragment</name>
        <artwork><![CDATA[
Input: "https://docs.example.com/guide#installation"
Output: "https://ypHTiw0JUMcr4bUjQH9Dxo8wGWHyfFlLq8VrOE-zX6IbgLFxYX_Jm
         2hzivywvrpIBWa-9Jl6nSZLq2pd35QwkDsc1-_Kao2BvyBB19ndu1PpwQv1wy
         uA"
]]></artwork>
      </section>
      <section anchor="test-vector-8-uri-with-query-and-fragment">
        <name>Test Vector 8: URI with Query and Fragment</name>
        <artwork><![CDATA[
Input: "/api/v2/users?id=123#profile"
Output: "/b9bCOhqZsvU9XxGOMk6d8QFQwcP2C3bJVNVZDge7zfub_ai4x6LaUlXp-XjZ
         XOgZlLloIbasK-JKlbeKeKV2rctq5bX9zQh1KogN2zaggTMZioUb4kwGIKp8Z
         y744xQwGDG64n6GhN56XEM8LvBfJuEj6ZgsjeLbTPIMbCmO0pJhzVSh"
]]></artwork>
      </section>
    </section>
  </back>
  <!-- ##markdown-source:
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
7qUm+NtA+1SyvcwYuk/MYd0VdVZ7rBYpsLW6wS5VO88WL5wedvKJVrhIiwCy
ibSFvyspVHcf0mK55IcJIXahPl1wQMXmlOqizQvMvyhlFkQDlU/mgE7lHNXU
Kh50KO5qMUBLl+pzeZRMDiuflqXky7NyO9GxrdgJVg8+NAfUcgHV4oi1iFI+
m+Hn7sIahcHrP7d/8WyonNmF2eySgPaGq9PZnO2qxDZn8K2BKjhii8P2MS/I
cXNG4WKKJFybxzHvXJ00WVR88dOiL2wDhpzWSsuWFY+aXMeOPbVWXRG7p51W
twB3y1l29gCLJN+HZdbZIznT48pdGdJq2cMEPHOxpbKGlN59I/0PK2d2uUYp
Gapsuj+mgFnOk0AKv27mg1sEz02CUK4eTX1V9pLJ/KNiOkjvGs5K67+fKj8s
C1ALoeYVQxBOPMsI2FqcD+iVxVB6I1JJhdWF/xypUNdR1LVBXAVLSoC/p/60
o35K3bOvXkmkAp3Oy3a2Tqe1UHxmmgPpenON4DAW2Rxs1bMUCLH8YrlaAbUv
aLU2adml3DLL2KmsaGoY3ql2UNKPhOIFp/AjVxwUN8D2GCnWqUaXcmh7LB7q
0L8KjJLExemxlotjFyjVfnMmkb+1uLRSUFt+jI8NN5cr3z0yrehJ4stFrUio
puKQBppyonshJsVW2VwBDZmJ2hReWYN3GpTcMfE0EUUCug7HJzk2DQcHt07/
8aSlm1v+UyiGVoIMZjgjOv3dztdWsC+c492QbSLMHt8H2Uts9WS5bov/1hUv
4EQBWuqNz3NMoGRzlwkBws846QgkdwxfLwZncBFd8k72A3GKtzB0YqY8OziE
mINjXZfvedLkO07klmfEeVEbB7f1jmivtFfba+319kb7aftZ+3l7s+23e+1+
e9AO2sOVtielfnz5GAZUrvolTdesPeJbn+qtd+e15rteX1VlOHSb18ev4s1r
P/+81lu7TU+OTt5Fow1/dvbtYOP9s3cfNtd3nl2cjY6eT19/frO29mzr7dmH
49wcZv/i5jDfbeYXw5ebG2fp2eudo9H21ceL6PVK/moyufyUbU+yj2df3+69
Pup/evPmpjd4ORgPP3xb2aze1GrHfazb3VlpG096m72d4/HXz+n1xebH21fH
h1dPB8/fvXw3Pt8f7F/efno7G6Srkw8fZ593pulG0/+2eZMNL4bZWhL0zTbW
Bpufri7i9tGz9/3b443Dj8PbvY2LuHc9On/z8fnF7HRwdFS93rWOOESrYdMI
T7j+6gPpD6KWfSgYKBCnT1bbqxtP2mv0tnZrOluvOKbx7cXh6tFadnvz6cur
2y/XO7MP0Vr77Ft0e9q+bg93e1dn70Y75x93LvJnp1+fh2e9DyfrZn/t4+tP
t2/TzW87a1c3K2+Ovm311z/kX56dfr4+HbTfX8ySNx8O0o23b06iD+nW5ttR
futfvnn28To8P1t7ZcY5/zpsfvw8OLn9NP+68mnl8OP7vc3L9fjL65vp0y/b
uxdX77Px1lV+FGzsfL26HD8dVANtvSNO8SFrrrFDDPx+DP4TyFu9io2OOKOr
XnFiFW/w3o9zJ+f54SR993Z3e7zV6z979uY6HZx+aV/dhi/DWR5tnb+bTg0s
d66OxltBe/vkZHXv67v49vh0b/LpMr8aXnw6S5LN6f7hMDze/BL7a2+jjdOz
Z8HZ26R6I08p2Iuh+Y5CC050aMH9cE0DDPb96esLpDh/o3yoFyvtPwHt/mR1
ezvLP0xvz9N9s9/9LxubV7CZYDf1v358+Sp6O/qw6R8cnK49/XR9Mdw8Pr06
jN7vbZycn16u7YbH62vjg80PXwZn+bODTTPMywUX8ZkFhJcylKJ684O4nzrX
cJTDPXxEFp4JRztW7H4+e30e3rTfXBz2k/XexZd3rzd3b+PnN68+vJ4PX04O
vj5/nxzvNb99fLrfGx28vP308fKNddqr42/h9fzmOpntb3/wm5tvJk+js88H
X1dng7WNdzdXu2l/pXn51o9Xt6/n29srm9EgXzmZ3by7XrmZm2HyrerdPy+h
AApG1XB44s/CJ9erT/IU8OOncPBiZXXt0SyJkfw8BM1v+ierO2u9N++P3n/e
HQXPvg3z3qUfrt8+PfAvJh9nzY9fPpsVfzwefZ4cTOJ9YN5vm2/eTnrB2+Dt
+9Wkn33d6H3c/PZuvPI2Hh2tfvNHo/PDz2F80Vu/unm1/3b23Bpm/mx9/fbd
zavdV0/Xo6evxkcbTz/uHT4/uN4evsn3vjz9PEq/BAe985P9w97O9Lg9ezP+
9v5szND6b8BMDmUCowAA

-->

</rfc>
