<?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-00" 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-00"/>
    <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.</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 encrypting
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, typically separated by
‘/’ characters. For encryption purposes, components include the
trailing separator 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 hash function that can
produce output of arbitrary length.</t>
        </li>
        <li>
          <t>SIV: Synthetic Initialization Vector, a 16-byte value derived
from the accumulated state of all previous components, used for
authentication and as input to keystream generation.</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>
      <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.</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>Note that all components except the last include the trailing ‘/’
character. This ensures proper reconstruction during decryption.</t>
        </section>
        <section anchor="path-only-uris">
          <name>Path-Only URIs</name>
          <t>For URIs without a scheme:</t>
          <artwork><![CDATA[
Input:  "/a/b/c"

Components:

- Scheme: "" (empty)
- Component 1: "a/"
- Component 2: "b/"
- Component 3: "c"
]]></artwork>
          <t>The leading ‘/’ is not treated as a separate component but is
implied during reconstruction.</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>
      </section>
    </section>
    <section anchor="cryptographic-operations">
      <name>Cryptographic Operations</name>
      <t>URICrypt uses three parallel TurboSHAKE128 <xref target="I-D.draft-irtf-cfrg-kangarootwelve"/> instances for different
purposes, all initialized from the same base hasher.</t>
      <section anchor="hasher-init">
        <name>Hasher Initialization</name>
        <t>The base hasher is initialized with the secret key and context
parameter using length-prefixed encoding to prevent ambiguities.</t>
        <t>Two hashers are derived from the base hasher:</t>
        <ol spacing="normal" type="1"><li>
            <t>Components Hasher: Updated with each component’s plaintext to
generate SIVs</t>
          </li>
          <li>
            <t>Base Keystream Hasher: Used as the starting point for generating
keystream for each component</t>
          </li>
        </ol>
        <t>The initialization process:</t>
        <artwork><![CDATA[
base_hasher = TurboSHAKE128()
base_hasher.update(len(secret_key))
base_hasher.update(secret_key)
base_hasher.update(len(context))
base_hasher.update(context)

components_hasher = base_hasher.clone()
components_hasher.update("IV")

base_keystream_hasher = base_hasher.clone()
base_keystream_hasher.update("KS")
]]></artwork>
      </section>
      <section anchor="component-encryption">
        <name>Component Encryption</name>
        <t>For each component, the encryption process is:</t>
        <ol spacing="normal" type="1"><li>
            <t>Update <tt>components_hasher</tt> with the component plaintext</t>
          </li>
          <li>
            <t>Generate SIV from <tt>components_hasher</tt> (16 bytes)</t>
          </li>
          <li>
            <t>Create <tt>keystream_hasher</tt> by cloning <tt>base_keystream_hasher</tt> and updating with SIV</t>
          </li>
          <li>
            <t>Calculate padding needed for base64 encoding</t>
          </li>
          <li>
            <t>Generate keystream of length <tt>(component_length + padding)</tt></t>
          </li>
          <li>
            <t>XOR padded component with keystream</t>
          </li>
          <li>
            <t>Output SIV concatenated with <tt>encrypted_component</tt></t>
          </li>
        </ol>
        <t>The padding length is calculated as:
<tt>padding_len = (3 - (16 + component_len) % 3) % 3</tt></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 (16 bytes)</t>
          </li>
          <li>
            <t>Create <tt>keystream_hasher</tt> by cloning <tt>base_keystream_hasher</tt> and updating with SIV</t>
          </li>
          <li>
            <t>Read encrypted component data (length determined from encoding)</t>
          </li>
          <li>
            <t>Generate keystream and decrypt component</t>
          </li>
          <li>
            <t>Remove padding to recover plaintext</t>
          </li>
          <li>
            <t>Update <tt>components_hasher</tt> with plaintext</t>
          </li>
          <li>
            <t>Generate expected SIV from <tt>components_hasher</tt></t>
          </li>
          <li>
            <t>Compare expected SIV with received SIV (constant-time)</t>
          </li>
          <li>
            <t>If mismatch, return <tt>error</tt></t>
          </li>
        </ol>
        <t>Any tampering with the encrypted data will cause the SIV comparison to fail.</t>
      </section>
      <section anchor="padding-and-encoding">
        <name>Padding and Encoding</name>
        <t>Each encrypted component <tt>(SIV || ciphertext)</tt> is padded to make its
length a multiple of 3 bytes, enabling clean base64 encoding without
padding characters.</t>
        <t>The final output is encoded using URL-safe base64 <xref target="RFC4648"/> without padding.</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 hash 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 hash state. The data is processed sequentially and updates the internal state of the hash function.</t>
        </li>
        <li>
          <t><tt>.read(length)</tt>: Squeezes the specified number of bytes from the hash function’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 hash 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>concatenated with: 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 using ‘/’ as the separator between components.</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 hashers as described in <xref target="hasher-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><tt>Update components_hasher with component</tt></t>
              </li>
              <li>
                <t><tt>SIV = components_hasher.read(16)</tt></t>
              </li>
              <li>
                <t><tt>keystream_hasher = base_keystream_hasher.clone()</tt></t>
              </li>
              <li>
                <t><tt>keystream_hasher.update(SIV)</tt></t>
              </li>
              <li>
                <t><tt>padding_len = (3 - (16 + len(component)) % 3) % 3</tt></t>
              </li>
              <li>
                <t><tt>keystream = keystream_hasher.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>Return <tt>scheme + 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>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 hashers as described in <xref target="hasher-init"/></t>
          </li>
          <li>
            <t><tt>decrypted_components = empty list</tt></t>
          </li>
          <li>
            <t><tt>input_stream = Stream(decoded)</tt></t>
          </li>
          <li>
            <t>While input_stream is not empty:
            </t>
            <ul spacing="normal">
              <li>
                <t><tt>SIV = input_stream.read(16)</tt>. If not enough bytes, return <tt>error</tt></t>
              </li>
              <li>
                <t><tt>keystream_hasher = base_keystream_hasher.clone()</tt></t>
              </li>
              <li>
                <t><tt>keystream_hasher.update(SIV)</tt></t>
              </li>
              <li>
                <t>Determine component length from remaining data and padding</t>
              </li>
              <li>
                <t><tt>encrypted_part = input_stream.read(component_length)</tt></t>
              </li>
              <li>
                <t><tt>keystream = keystream_hasher.read(len(encrypted_part))</tt></t>
              </li>
              <li>
                <t><tt>padded_plaintext = encrypted_part XOR keystream</tt></t>
              </li>
              <li>
                <t><tt>component = remove_padding(padded_plaintext)</tt></t>
              </li>
              <li>
                <t>Update <tt>components_hasher</tt> with component</t>
              </li>
              <li>
                <t><tt>expected_SIV = components_hasher.read(16)</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 hasher</t>
          </li>
          <li>
            <t>Generating keystream for encryption/decryption</t>
          </li>
          <li>
            <t>All hash operations in the initialization</t>
          </li>
        </ul>
        <t>TurboSHAKE128 is specified in <xref target="I-D.draft-irtf-cfrg-kangarootwelve"/> 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 16 bytes long. Keys shorter than 16
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
hasher:</t>
        <artwork><![CDATA[
base_hasher.update(len(secret_key) as uint8)
base_hasher.update(secret_key)
base_hasher.update(len(context) as uint8)
base_hasher.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:</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:
- URIs sharing a common prefix will produce ciphertexts with the same encrypted prefix
- This property is deterministic and intentional, enabling systems to perform prefix-based operations
- The leakage is limited to prefix structure only - no information about non-matching suffixes is revealed</t>
      </section>
      <section anchor="domain-separation">
        <name>Domain Separation</name>
        <t>The context parameter provides cryptographic domain separation:
- Different contexts with the same key produce completely independent ciphertexts
- This prevents cross-context attacks where ciphertexts from one application could be used in another
- Context binding is cryptographically enforced through the hasher initialization</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: 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 in 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:</t>
        <ul spacing="normal">
          <li>
            <t>Key strength: Minimum 128-bit security with 16-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:</t>
        <ul spacing="normal">
          <li>
            <t>Deterministic encryption: Same inputs produce same outputs, enabling certain traffic analysis</t>
          </li>
          <li>
            <t>Length preservation: Component lengths are not hidden, potentially revealing information patterns</t>
          </li>
          <li>
            <t>Prefix structure leakage: The hierarchical structure of URIs is preserved by design</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 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 Leakage: The length of each component is preserved in the
encrypted output. Applications sensitive to length information
SHOULD consider 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 anchor="iana-considerations">
      <name>IANA Considerations</name>
      <t>This document has no actions for IANA.</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="I-D.draft-irtf-cfrg-kangarootwelve">
        <front>
          <title>KangarooTwelve and TurboSHAKE</title>
          <author fullname="Benoît Viguier" initials="B." surname="Viguier">
            <organization>ABN AMRO Bank</organization>
          </author>
          <author fullname="David Wong" initials="D." surname="Wong">
            <organization>zkSecurity</organization>
          </author>
          <author fullname="Gilles Van Assche" initials="G." surname="Van Assche">
            <organization>STMicroelectronics</organization>
          </author>
          <author fullname="Quynh Dang" initials="Q." surname="Dang">
            <organization>National Institute of Standards and Technology</organization>
          </author>
          <author fullname="Joan Daemen" initials="J." surname="Daemen">
            <organization>Radboud University</organization>
          </author>
          <date day="21" month="February" year="2025"/>
          <abstract>
            <t>   This document defines four eXtendable Output Functions (XOF), hash
   functions with output of arbitrary length, named TurboSHAKE128,
   TurboSHAKE256, KT128 and KT256.

   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.

   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 [FIPS 202], and is meant to serve as a stable
   reference and an implementation guide.

            </t>
          </abstract>
        </front>
        <seriesInfo name="Internet-Draft" value="draft-irtf-cfrg-kangarootwelve-17"/>
      </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="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 502?>

<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

  if path starts with "/":
     path = substring after first "/"

  components = []
  while path is not empty:
     slash_pos = find("/", path)
     if slash_pos found:
        component = substring(0, slash_pos + 1)
        path = substring(slash_pos + 1)
        components.append(component)
     else:
        components.append(path)
        path = ""

  return (scheme, components)
]]></artwork>
      </section>
      <section anchor="hasher-initialization">
        <name>Hasher Initialization</name>
        <artwork><![CDATA[
function initialize_hashers(secret_key, context):
  // Initialize base hasher
  base_hasher = TurboSHAKE128(0x1F)

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

  // Create components hasher
  components_hasher = base_hasher.clone()
  components_hasher.update("IV")

  // Create base keystream hasher
  base_keystream_hasher = base_hasher.clone()
  base_keystream_hasher.update("KS")

  return (components_hasher, base_keystream_hasher)
]]></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 hashers with secret key and context
  (components_hasher, base_keystream_hasher) =
      initialize_hashers(secret_key, context)
  if error: return error

  encrypted_output = byte_array()

  // Process each component
  for component in components:
     // Update components hasher for SIV computation
     components_hasher.update(component)

     // Generate 16-byte Synthetic Initialization Vector (SIV)
     siv = components_hasher.squeeze(16)

     // Create keystream hasher for this component
     keystream_hasher = base_keystream_hasher.clone()
     keystream_hasher.update(siv)

     // Calculate padding for base64 encoding alignment
     component_len = len(component)
     padding_len = (3 - (16 + component_len) % 3) % 3

     // Generate keystream
     keystream = keystream_hasher.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 scheme
  return scheme + 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:
     scheme = ""
     base64_part = encrypted_uri

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

  // Initialize hashers with secret key and context
  (components_hasher, base_keystream_hasher) =
      initialize_hashers(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(16)
     if len(siv) != 16:
        return error("Decryption failed")

     // Create keystream hasher
     keystream_hasher = base_keystream_hasher.clone()
     keystream_hasher.update(siv)

     // Determine component length by checking padding constraints
     remaining = input_stream.remaining()
     if remaining == 0:
        return error("Decryption failed")

     // Find valid component length by checking padding alignment
     component_data = None
     for possible_len in range(1, remaining + 1):
        total_len = 16 + possible_len
        padding_len = (3 - total_len % 3) % 3
        if possible_len >= padding_len:
           component_data = input_stream.peek(possible_len)
           break

     if component_data is None:
        return error("Decryption failed")

     // Read encrypted data
     encrypted_part = input_stream.read(len(component_data))

     // Generate keystream and decrypt
     keystream = keystream_hasher.squeeze(len(encrypted_part))
     padded_plaintext = xor_bytes(encrypted_part, keystream)

     // Remove padding bytes added for base64 alignment
     padding_len = (3 - (16 + len(encrypted_part)) % 3) % 3
     component = padded_plaintext[:-padding_len] if padding_len > 0 else padded_plaintext

     // Update hasher with plaintext
     components_hasher.update(component)

     // Generate expected SIV
     expected_siv = components_hasher.squeeze(16)

     // 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
  if scheme and decrypted_components:
     path = "".join(decrypted_components)
     return scheme + path
  elif decrypted_components:
     return "/" + "".join(decrypted_components)
  else:
     return ""
]]></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
  return (3 - (16 + component_len) % 3) % 3

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://HOGo9vauZ3b3xsPNPQng5apSzL5V7QW94C7USgN8mHZJ337AKSWOucUwMuD-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-az9wtfUft3rec3d9YkUo0N7VcxO5MXfxE5UobvgTJX8UpRdNN"
]]></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://hxUM2N3txwYjGxjvCpWn30SznxR0v0fDbkSQgCTXCUu7Rq8iSbWP40OvYxKs9zC3kw1JNzAc4Wuj7RZvRd0VUprJWLs5KJPnWsA9Kguxa_J7XviTS3GTqf-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: "/b9bCOhqZsvU9XxGOMk6d8QFQPTuMlsQKDBhAbc77JvsdRj0kxiFipunATQmmCkNhAe0BPP2EqQoxORElY_ukfUYSrr9mIMfiO9joa3Kn5RS7eSKr"
]]></artwork>
      </section>
    </section>
  </back>
  <!-- ##markdown-source:
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
GHx97FOrV4exwlJIqqGMpDjDd1mmQUd85FenSe0Hyd8NFlM6nPq5J5VpyHN+
v+hM5V13IPpb/e3+Tn+3/7D/qP+4/6S/14/7w/6on/Rlf7zVj3R4MBBrCLmp
FTo94d0RX5Bo3nkZvHHxvnf4mSlMu+blyS/53m1c/bYz3LlTp8en77LJw3hx
/vXtw18fv3u/t3vw+PJ8cvxk/vK31zs7j/ffnL8/qUaXX46qw151OX6x9/Bc
nb88OJ48u/lwmb3cqn6Zza4+ls9m5Yfzz2+evzwefXz9+ssweZFMx++/bu21
X2V7EL5eMrxPA/jN4d7w4GT6+Td1e7n34e6Xk6ObR8mTdy/eTS9eJa+u7j6+
WSRqe/b+w+K3g7l62Iu/7n0px5fjcqeQo51k7+PNZd4/fvzr6O7k4dGH8d3z
h5f58HZy8frDk8vFWXJ83A7lzkC/Jd0VuhDqduSPkmzDJwA2X+RqE/9rLpv9
nc0xWMSN+WK3hSTTu8uj7eOd8u7Lx0+/3H26PVi8z3b651+zu7P+bX98OLw5
fzc5uPhwcFk9Pvv8JD0fvj/d7Z/cfrx7o/a+HuzcfNl6ffx1f7T7vvr0+Oy3
27Ok/+vlonj9/q16+Ob1afZe7e+9mVR38dXrxx9u04vznV8uPo97H35LTu8+
Lj9vfdw6+vDr872r3fzTyy/zR5+eHV7e/FpO92+qY/nw4PPN1fRR0o6g3YE4
y3MtJfx6z29z5r/BlO1QPByIcxLhFupsomXfLPNN80b07+Gq04vqaKbevTl8
Nt0fjh4/fn2rkrNP/Zu79EW6qLL9i3fz+cHN8XRf9p+dnm4///wuvzs5ez77
eFXdjC8/nhfF3vzV0Tg92fuUxztvsodn54/l+ZuCwf8faR7hE/doAAA=

-->

</rfc>
