Skip to main content

Random-Access Authenticated Encryption
draft-sullivan-cfrg-raae-02

The information below is for an old version of the document.
Document Type
This is an older version of an Internet-Draft whose latest revision state is "Active".
Author Nick Sullivan
Last updated 2026-07-13 (Latest revision 2026-07-06)
RFC stream (None)
Formats
Stream Stream state (No stream defined)
Consensus boilerplate Unknown
RFC Editor Note (None)
IESG IESG state I-D Exists
Telechat date (None)
Responsible AD (None)
Send notices to (None)
draft-sullivan-cfrg-raae-02
Crypto Forum                                                 N. Sullivan
Internet-Draft                               Cryptography Consulting LLC
Intended status: Informational                              13 July 2026
Expires: 14 January 2027

                 Random-Access Authenticated Encryption
                      draft-sullivan-cfrg-raae-02

Abstract

   This document defines random-access authenticated encryption (raAE),
   a primitive that partitions a message into an indexed sequence of
   segments that can be encrypted and decrypted independently and in any
   order.  It also specifies SEAL (Segmented Encryption and
   Authentication Layer), a parameterized construction that defines a
   family of concrete raAE instantiations, one for each valid choice of
   an Authenticated Encryption with Associated Data (AEAD) algorithm, a
   key derivation function (KDF), and associated parameters.

   SEAL provides two profiles, immutable (write-once) and mutable (in-
   place ciphertext rewrite), each with per-segment authentication.  A
   separately configured snapshot authenticator can additionally
   authenticate the complete, indexed segment set.

   The document also defines the security notions of raAE, specifies the
   requirements for conforming constructions, analyzes SEAL against
   those requirements, and provides example cipher suites and test
   vectors.

Discussion Venues

   This note is to be removed before publishing as an RFC.

   Discussion of this document takes place on the Crypto Forum Research
   Group mailing list (cfrg@ietf.org), which is archived at
   https://mailarchive.ietf.org/arch/browse/cfrg.

   Source for this draft and an issue tracker can be found at
   https://github.com/grittygrease/draft-sullivan-cfrg-raae.

Status of This Memo

   This Internet-Draft is submitted in full conformance with the
   provisions of BCP 78 and BCP 79.

Sullivan                 Expires 14 January 2027                [Page 1]
Internet-Draft                    raAE                         July 2026

   Internet-Drafts are working documents of the Internet Engineering
   Task Force (IETF).  Note that other groups may also distribute
   working documents as Internet-Drafts.  The list of current Internet-
   Drafts is at https://datatracker.ietf.org/drafts/current/.

   Internet-Drafts are draft documents valid for a maximum of six months
   and may be updated, replaced, or obsoleted by other documents at any
   time.  It is inappropriate to use Internet-Drafts as reference
   material or to cite them other than as "work in progress."

   This Internet-Draft will expire on 14 January 2027.

Copyright Notice

   Copyright (c) 2026 IETF Trust and the persons identified as the
   document authors.  All rights reserved.

   This document is subject to BCP 78 and the IETF Trust's Legal
   Provisions Relating to IETF Documents (https://trustee.ietf.org/
   license-info) in effect on the date of publication of this document.
   Please review these documents carefully, as they describe your rights
   and restrictions with respect to this document.

Table of Contents

   1.  Introduction  . . . . . . . . . . . . . . . . . . . . . . . .   5
     1.1.  Related Work  . . . . . . . . . . . . . . . . . . . . . .   7
   2.  Conventions and Terminology . . . . . . . . . . . . . . . . .   8
     2.1.  Taxonomy  . . . . . . . . . . . . . . . . . . . . . . . .   9
     2.2.  Notation  . . . . . . . . . . . . . . . . . . . . . . . .  10
   3.  The raAE Primitive  . . . . . . . . . . . . . . . . . . . . .  11
     3.1.  Overview  . . . . . . . . . . . . . . . . . . . . . . . .  11
     3.2.  Interface . . . . . . . . . . . . . . . . . . . . . . . .  12
     3.3.  Extended Interface  . . . . . . . . . . . . . . . . . . .  12
   4.  SEAL: A Concrete raAE Construction  . . . . . . . . . . . . .  14
     4.1.  Construction Overview . . . . . . . . . . . . . . . . . .  16
     4.2.  Component Suite and Parameters  . . . . . . . . . . . . .  16
       4.2.1.  Suite Choices . . . . . . . . . . . . . . . . . . . .  18
       4.2.2.  Profile-Level Constants . . . . . . . . . . . . . . .  19
       4.2.3.  Algorithm-Determined Sizes  . . . . . . . . . . . . .  19
       4.2.4.  Per-Message Inputs  . . . . . . . . . . . . . . . . .  19
     4.3.  The KDF Combiner  . . . . . . . . . . . . . . . . . . . .  20
       4.3.1.  Two-Step KDF  . . . . . . . . . . . . . . . . . . . .  22
       4.3.2.  One-Step KDF  . . . . . . . . . . . . . . . . . . . .  22
     4.4.  SEAL Encodings and Labels . . . . . . . . . . . . . . . .  23
       4.4.1.  Payload Info Construction . . . . . . . . . . . . . .  23
       4.4.2.  Segment AAD . . . . . . . . . . . . . . . . . . . . .  24
       4.4.3.  Labels  . . . . . . . . . . . . . . . . . . . . . . .  26

Sullivan                 Expires 14 January 2027                [Page 2]
Internet-Draft                    raAE                         July 2026

     4.5.  Key Schedule and Nonce Generation . . . . . . . . . . . .  27
       4.5.1.  Payload Schedule  . . . . . . . . . . . . . . . . . .  27
       4.5.2.  Epoch Key Derivation  . . . . . . . . . . . . . . . .  28
       4.5.3.  Nonce Generation  . . . . . . . . . . . . . . . . . .  28
     4.6.  Commitment  . . . . . . . . . . . . . . . . . . . . . . .  31
     4.7.  Snapshot Authenticator  . . . . . . . . . . . . . . . . .  32
       4.7.1.  Interface and Requirements  . . . . . . . . . . . . .  32
       4.7.2.  Selecting an Authenticator  . . . . . . . . . . . . .  35
       4.7.3.  None  . . . . . . . . . . . . . . . . . . . . . . . .  35
       4.7.4.  Masked Multiset Hash  . . . . . . . . . . . . . . . .  35
       4.7.5.  Digest Transcript . . . . . . . . . . . . . . . . . .  38
       4.7.6.  Epoch Digest Tree . . . . . . . . . . . . . . . . . .  41
     4.8.  Segment Algorithms  . . . . . . . . . . . . . . . . . . .  45
       4.8.1.  EncryptSegment  . . . . . . . . . . . . . . . . . . .  47
       4.8.2.  DecryptSegment  . . . . . . . . . . . . . . . . . . .  47
       4.8.3.  RewriteSegment  . . . . . . . . . . . . . . . . . . .  47
     4.9.  Top-level Algorithms  . . . . . . . . . . . . . . . . . .  48
       4.9.1.  Read-Only Operations  . . . . . . . . . . . . . . . .  48
       4.9.2.  Rewritable Operations . . . . . . . . . . . . . . . .  52
     4.10. SEAL Suites . . . . . . . . . . . . . . . . . . . . . . .  55
       4.10.1.  Algorithms . . . . . . . . . . . . . . . . . . . . .  56
       4.10.2.  Composing a SEAL Suite . . . . . . . . . . . . . . .  59
     4.11. Serialization Layouts . . . . . . . . . . . . . . . . . .  61
       4.11.1.  Linear Layout  . . . . . . . . . . . . . . . . . . .  61
       4.11.2.  Aligned Layout . . . . . . . . . . . . . . . . . . .  62
       4.11.3.  Split Layout . . . . . . . . . . . . . . . . . . . .  65
       4.11.4.  Immutable-Profile Layouts  . . . . . . . . . . . . .  66
     4.12. SEAL Named Instantiations . . . . . . . . . . . . . . . .  66
   5.  Security Analysis . . . . . . . . . . . . . . . . . . . . . .  69
     5.1.  Operational Summary . . . . . . . . . . . . . . . . . . .  69
     5.2.  Security Notions  . . . . . . . . . . . . . . . . . . . .  70
       5.2.1.  ra-ROR  . . . . . . . . . . . . . . . . . . . . . . .  70
       5.2.2.  ra-CMT  . . . . . . . . . . . . . . . . . . . . . . .  71
       5.2.3.  Snapshot Authentication . . . . . . . . . . . . . . .  71
     5.3.  Relations to Other Notions  . . . . . . . . . . . . . . .  72
     5.4.  Adversary Model and Assumptions . . . . . . . . . . . . .  73
     5.5.  raAE Construction Requirements  . . . . . . . . . . . . .  74
     5.6.  Segment Confidentiality and Integrity . . . . . . . . . .  77
     5.7.  Commitment Security . . . . . . . . . . . . . . . . . . .  78
     5.8.  Snapshot Authenticator Security . . . . . . . . . . . . .  80
       5.8.1.  Masked Multiset Hash  . . . . . . . . . . . . . . . .  82
       5.8.2.  Digest Transcript . . . . . . . . . . . . . . . . . .  82
       5.8.3.  Epoch Digest Tree . . . . . . . . . . . . . . . . . .  84
     5.9.  Capacity and Usage Limits . . . . . . . . . . . . . . . .  85
       5.9.1.  Accounting Model  . . . . . . . . . . . . . . . . . .  85
       5.9.2.  Limit Classes . . . . . . . . . . . . . . . . . . . .  86
       5.9.3.  Binding Limits for Profiled Suites  . . . . . . . . .  87
       5.9.4.  Maximum Write-Once Object Size  . . . . . . . . . . .  88

Sullivan                 Expires 14 January 2027                [Page 3]
Internet-Draft                    raAE                         July 2026

       5.9.5.  Rewrite Capacity  . . . . . . . . . . . . . . . . . .  89
       5.9.6.  Choosing epoch_length . . . . . . . . . . . . . . . .  91
       5.9.7.  Derivations . . . . . . . . . . . . . . . . . . . . .  92
   6.  Security Considerations . . . . . . . . . . . . . . . . . . .  94
     6.1.  Detection Summary . . . . . . . . . . . . . . . . . . . .  94
     6.2.  Nonce Misuse  . . . . . . . . . . . . . . . . . . . . . .  96
     6.3.  Parameter Set Mismatch  . . . . . . . . . . . . . . . . .  97
     6.4.  Framing and Label Errors  . . . . . . . . . . . . . . . .  97
     6.5.  Parameter Misuse  . . . . . . . . . . . . . . . . . . . .  97
     6.6.  Snapshot Integrity Limitations  . . . . . . . . . . . . .  98
     6.7.  Salt Reuse  . . . . . . . . . . . . . . . . . . . . . . .  99
     6.8.  Rewrite Hazards . . . . . . . . . . . . . . . . . . . . . 100
     6.9.  Constant-Time Implementation  . . . . . . . . . . . . . . 101
     6.10. Properties Not Provided . . . . . . . . . . . . . . . . . 102
     6.11. Cross-Application Domain Separation . . . . . . . . . . . 102
   7.  IANA Considerations . . . . . . . . . . . . . . . . . . . . . 103
   8.  References  . . . . . . . . . . . . . . . . . . . . . . . . . 103
     8.1.  Normative References  . . . . . . . . . . . . . . . . . . 103
     8.2.  Informative References  . . . . . . . . . . . . . . . . . 105
   Appendix A.  Optional Plaintext-Bound Nonce Construction  . . . . 107
   Appendix B.  Optional Hedged Randomness . . . . . . . . . . . . . 108
   Appendix C.  Proof Status and Security Claim Provenance . . . . . 109
     C.1.  Provenance of the Security Claims . . . . . . . . . . . . 110
     C.2.  Advantage Notation and Adversary Parameters . . . . . . . 112
     C.3.  ra-ROR Reduction  . . . . . . . . . . . . . . . . . . . . 114
     C.4.  Commitment  . . . . . . . . . . . . . . . . . . . . . . . 114
     C.5.  Snapshot Authenticator  . . . . . . . . . . . . . . . . . 115
     C.6.  Nonce Modes . . . . . . . . . . . . . . . . . . . . . . . 117
   Appendix D.  Design Rationale . . . . . . . . . . . . . . . . . . 117
     D.1.  Key Schedule Design Rationale . . . . . . . . . . . . . . 117
     D.2.  Nonce Mode Design Rationale . . . . . . . . . . . . . . . 118
     D.3.  Snapshot Authenticator Design Rationale . . . . . . . . . 118
   Appendix E.  SEAL-simple Implementation Sketch  . . . . . . . . . 120
   Appendix F.  Test Vectors . . . . . . . . . . . . . . . . . . . . 122
     F.1.  Single Segment, AES-256-GCM, HKDF-SHA-256, 16384  . . . . 124
     F.2.  Commitment with Global Associated Data  . . . . . . . . . 126
     F.3.  KDF Combiner Vectors  . . . . . . . . . . . . . . . . . . 127
     F.4.  Single Segment, AES-256-GCM, HKDF-SHA-256, 65536  . . . . 128
     F.5.  Single Segment, ChaCha20-Poly1305, HKDF-SHA-256, 16384  . 130
     F.6.  Single Segment, ChaCha20-Poly1305, HKDF-SHA-256, 65536  . 131
     F.7.  Single Segment, AEGIS-256, HKDF-SHA-256, 16384  . . . . . 132
     F.8.  Single Segment, AEGIS-256, HKDF-SHA-256, 65536  . . . . . 134
     F.9.  Two Segment, AES-256-GCM, HKDF-SHA-256, 16384 . . . . . . 135
     F.10. Two Segment, AES-256-GCM, HKDF-SHA-256, 65536 . . . . . . 137
     F.11. Two Segment, ChaCha20-Poly1305, HKDF-SHA-256, 16384 . . . 138
     F.12. Two Segment, ChaCha20-Poly1305, HKDF-SHA-256, 65536 . . . 140
     F.13. Two Segment, AEGIS-256, HKDF-SHA-256, 16384 . . . . . . . 141
     F.14. Two Segment, AEGIS-256, HKDF-SHA-256, 65536 . . . . . . . 143

Sullivan                 Expires 14 January 2027                [Page 4]
Internet-Draft                    raAE                         July 2026

     F.15. TurboSHAKE-256 Cipher Suite Vectors . . . . . . . . . . . 145
       F.15.1.  Single Segment, AES-256-GCM, TurboSHAKE-256,
               65536 . . . . . . . . . . . . . . . . . . . . . . . . 145
       F.15.2.  Two Segment, AES-256-GCM, TurboSHAKE-256, 65536  . . 146
     F.16. Segment Rewrite Vector  . . . . . . . . . . . . . . . . . 148
       F.16.1.  Two Segment, AES-256-GCM, HKDF-SHA-256, 65536
               (Rewrite) . . . . . . . . . . . . . . . . . . . . . . 148
     F.17. Derived-Nonce Cipher Suite Vector . . . . . . . . . . . . 151
       F.17.1.  Two Segment, AES-256-GCM-SIV, HKDF-SHA-256, 65536
               (Rewrite) . . . . . . . . . . . . . . . . . . . . . . 151
     F.18. Cross-Epoch Key Vector  . . . . . . . . . . . . . . . . . 154
       F.18.1.  Two Segment, AES-256-GCM, HKDF-SHA-256, 65536,
               epoch_length 0  . . . . . . . . . . . . . . . . . . . 154
     F.19. Plaintext-Bound Nonce Component Vectors . . . . . . . . . 156
       F.19.1.  AES-256-GCM, HKDF-SHA-256, 16384, Segment 0  . . . . 156
       F.19.2.  AES-256-GCM, TurboSHAKE-256, 65536, Segment 1  . . . 156
     F.20. Digest Transcript Component Vectors . . . . . . . . . . . 157
       F.20.1.  Leaves and Transcript, n_seg = 2 . . . . . . . . . . 158
     F.21. Epoch Digest Tree Component Vectors . . . . . . . . . . . 159
     F.22. Negative SnapVerify Vector  . . . . . . . . . . . . . . . 160
       F.22.1.  Tampered Accumulator, Snapshot Tag Not Recomputed  . 160
     F.23. SEAL-simple(HKDF-SHA-256, AES-256-GCM)  . . . . . . . . . 161
   Acknowledgments . . . . . . . . . . . . . . . . . . . . . . . . . 163
   Author's Address  . . . . . . . . . . . . . . . . . . . . . . . . 163

1.  Introduction

   Large encrypted content is often stored as a sequence of fixed-size
   segments, so that an application can read or modify any part of it
   without processing the whole.  Encrypted backups, encrypted file
   formats, object stores with partial updates, and full-disk encryption
   over fixed-size blocks all work this way.  Such a system needs to
   encrypt, decrypt, or re-encrypt any individual segment on its own and
   in arbitrary order, and to verify that the stored object as a whole
   is authentic and complete, without re-encrypting the segments that
   did not change.

Sullivan                 Expires 14 January 2027                [Page 5]
Internet-Draft                    raAE                         July 2026

   This document specifies the raAE primitive (Section 3), whose base
   interface and security notions come from Fábrega et al.  ([FLRR25]),
   and defines SEAL (Segmented Encryption and Authentication Layer), a
   parameterized construction of raAE.  The base algorithms encrypt and
   decrypt segments in arbitrary order, so a caller can read or replace
   any segment on its own.  On top of that base, the extended raAE
   interface adds in-place rewrite and a snapshot, a stored value that
   authenticates the segment set as the writer last recorded it.  A
   rewrite re-encrypts only the changed segment and updates the
   snapshot, leaving every other segment untouched.  Appending to the
   end, or truncating from the end, composes the same per-segment
   operations, so the snapshot stays consistent without a separate
   algorithm.

   SEAL builds the primitive from a chosen AEAD algorithm, a key
   derivation function (KDF), and configuration parameters.  Under a
   snapshot authenticator, it binds every segment and the count into a
   public snapshot value that an adversary cannot forge without the
   content-derived key.

                     .------------------------------.
                     |        raAE primitive        |
                     |      abstract interface      |
                     '------------------------------'
                                   |
                                   v
      .--------------------------------------------------------------.
      |                      SEAL construction                       |
      |   parameterized by a cipher suite (AEAD + KDF), a maximum    |
      |   segment size, and an epoch length                          |
      |                                                              |
      |   .----------------------.    .----------------------.       |
      |   |  mutability:         |    |  snapshot:           |       |
      |   |  immutable | mutable |    |  none | present      |       |
      |   '----------------------'    '----------------------'       |
      '--------------------------------------------------------------'
                                   |
                                   v   provides
      .----------------------------.      .------------------------.
      |  per-segment authenticity  |      |   snapshot integrity   |
      |      (always present)      |      |     (when present)     |
      '----------------------------'      '------------------------'

           Figure 1: The raAE primitive and the SEAL construction

   raAE separates the two authentication scopes that applications often
   conflate:

Sullivan                 Expires 14 January 2027                [Page 6]
Internet-Draft                    raAE                         July 2026

   Per-segment authenticity:  One AEAD tag verifies under the segment
      index, a finality bit marking the last segment, and any caller-
      supplied associated data.  It does not establish that the segment
      belongs to the current snapshot.

   Snapshot integrity:  When a snapshot authenticator is configured,
      snapshot verification checks that the present segment set and the
      count are exactly what the writer last recorded, under a content-
      derived key.  It does not establish freshness against whole-object
      rollback, which a consuming protocol must supply.

   raAE and SEAL are deliberately distinct layers.  A consuming protocol
   supplies serialization, storage transactions, key management, and
   rollback protection, which are out of scope here.

   The remainder of this document presents raAE first and SEAL second.
   Section 1.1 situates raAE against prior segmented authenticated-
   encryption (AE) constructions.  Section 2 fixes terminology and
   notation.  Section 3 specifies the raAE primitive and its extended
   snapshot operations.  Section 4 defines SEAL.  Section 5 states the
   target security properties an raAE construction must meet, the
   assumptions SEAL's components must satisfy, and the operational
   limits on its use, and defines the security notions.  Cipher suites,
   serialization layouts, and named instantiations are in Section 4.10,
   Section 4.11, and Section 4.12, and the appendices provide the test
   vectors.

1.1.  Related Work

   CHAIN ([HRRV15]) is a segmented authenticated-encryption construction
   that chains state from one segment to the next.  It is sequential by
   construction and supports neither independent random-access reads nor
   in-place segment rewrites.

   STREAM ([HRRV15]) turns a nonce-based AEAD into a segmented
   construction using induced nonces that encode a message nonce, the
   segment index, and a final-segment bit.  STREAM supports random-
   access decryption and encryption of individual segments, but defines
   neither authenticated snapshots nor authenticated rewrites.  SEAL's
   derived nonce mode (Section 4.5.3.2) follows STREAM's counter-plus-
   final-bit structure and adds those operations.

   Tink Streaming AEAD ([Tink]) and OpenPGP v2 Symmetrically Encrypted
   and Integrity Protected Data (SEIPD) packets ([RFC9580]) are deployed
   STREAM-family formats.  Both derive a message key and nonce prefix
   from a key and a salt, encrypt each chunk under an AEAD nonce
   carrying the chunk index, and detect truncation with a final tag over
   the empty string.  Tink derives an AES-GCM key with the HMAC-based

Sullivan                 Expires 14 January 2027                [Page 7]
Internet-Draft                    raAE                         July 2026

   Key Derivation Function (HKDF) ([RFC5869]), while v2 SEIPD selects
   its cipher and AEAD from packet identifiers.  Each chunk is
   independently authenticated and can be verified locally at its
   boundary.  Neither format defines an in-place rewrite operation or a
   snapshot authenticator over an updatable set of segment tags.

   SEAL defines two types of snapshot authenticator.  The first is a
   keyed multiset hash over the segment tags, the MSet-XOR-Hash of
   Clarke, Devadas, van Dijk, Gassend, and Suh ([MSetHash]), published
   behind a deterministic mask this document adds (Section 4.7.4).  Each
   segment contributes a keyed KDF evaluation of its index and AEAD tag,
   the contributions XOR into an accumulator that any rewrite updates in
   O(1), and a domain-separated snapshot tag binds the segment count and
   the accumulator.  The second is a per-object keyed transcript over
   per-segment ciphertext digests (Section 4.7.5, Section 4.7.6),
   recomputed on any rewrite, whose content binding holds even against
   holders of the CEK, so a consuming protocol that authenticates the
   one snapshot value obtains per-segment origin authentication.

   Fábrega et al.  ([FLRR25]) formalized random-access AEAD security
   over a raAE primitive syntax in which segment encryption and
   decryption may be invoked in arbitrary order, and gave a
   construction, FLOE (Fast Lightweight Online Encryption), proved to
   meet it.  FLOE realizes the base interface: segments are written
   once, and there is no snapshot.  SEAL realizes that primitive with
   the extension of this document, and Section 5.2 defines the security
   notions used in its analysis.

   Each precedent covers part of the random-access AE design space.
   raAE is the abstract interface that generalizes the family with three
   capabilities none of them combines: arbitrary-order encryption, in-
   place authenticated rewrite, and an O(1) updatable snapshot over the
   whole segment set.  SEAL realizes all three in one construction.

2.  Conventions and Terminology

   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 [RFC2119] [RFC8174] when, and only when, they appear in all
   capitals, as shown here.

   A message segment is a unit of plaintext that is individually
   encrypted and authenticated.  Segments need not be uniform in length.
   A construction MAY set a maximum segment size, and any segment MAY be
   shorter than that maximum.

Sullivan                 Expires 14 January 2027                [Page 8]
Internet-Draft                    raAE                         July 2026

   A message is the content that one CEK and salt pair protects, the
   unit the primitive encrypts.  An object is that message in stored
   form: the ciphertext segments, their metadata, and any snapshot that
   a storage layer holds.  Each object carries one message, whose
   content may change through rewrite and length-changing operations.

   A ciphertext core is the AEAD encryption output corresponding to one
   message segment, excluding file-level headers and external segment
   metadata.  An AEAD-specific split divides this output into ct_i, the
   encrypted segment body, and tag_i, the authentication tag.

   A ciphertext segment is the stored representation of one encrypted
   message segment.  Depending on nonce_mode, it contains the ciphertext
   core and MAY also contain a stored nonce or other per-segment
   metadata.

   Segment metadata is per-segment information needed to decrypt or
   verify a ciphertext segment, such as a stored nonce, tag, finality
   status, or profile-defined values.

   This document writes AEAD.Encrypt and AEAD.Decrypt for the
   authenticated encryption and decryption operations defined in
   [RFC5116].

   A subscript denotes a per-segment value: X_i is the value X for the
   segment at index i, as in A_i, P_i, ct_i, and tag_i.  A value written
   f(i), as in segment_key(i) or nonce(i), is computed from the index by
   the named derivation.  X[a:b] is the octet range of X from offset a
   inclusive to offset b exclusive.

   The remaining terms (ikm, label, info, L, salt, CEK, protocol_id,
   nonce_mode, epoch, epoch_key, payload_key, segment_key, snap_key, and
   the commitment) are defined in Section 4 where they first appear.

2.1.  Taxonomy

   This document uses the following terms to keep the raAE primitive,
   the SEAL construction, profiles, and consuming protocols distinct.

   raAE primitive:  The abstract random-access AE interface and security
      target.

   Extended raAE interface:  The base interface plus snapshot
      maintenance and verification.

   SEAL construction:  The concrete construction specified in Section 4.

   Cipher suite:  The AEAD and KDF algorithms and their sizes.

Sullivan                 Expires 14 January 2027                [Page 9]
Internet-Draft                    raAE                         July 2026

   Profile:  A named set of fixed parameter choices, identified by a
      protocol_id, needed for interoperability (Section 4.10.2).

   Parameter set:  The per-object values that affect derivation and
      verification.

   Consuming protocol:  The protocol or format that stores objects and
      manages keys.

   Serialization:  The consuming protocol's wire or storage encoding.

   Segment:  A zero-based independently encrypted plaintext unit.

   Epoch:  A group of 2^epoch_length segment indices sharing one epoch
      key.

   Snapshot value:  The public value that, when a snapshot authenticator
      is configured, authenticates one segment set.

   Per-segment authenticity:  AEAD verification for one segment only.

   Snapshot integrity:  Verification of the complete segment set and
      snapshot.

   External freshness:  Rollback protection supplied outside raAE.

2.2.  Notation

   This document uses the following notation.

   I2OSP(n, w):  The w-octet big-endian encoding of the non-negative
      integer n, as defined in [RFC8017].

   uint8(n), uint16(n), uint32(n), uint64(n):  Shorthand for I2OSP(n,
      1), I2OSP(n, 2), I2OSP(n, 4), and I2OSP(n, 8) respectively.

   ||:  Octet string concatenation.

   XOR:  Bitwise exclusive-or of two octet strings of equal length.

   x[a:b]:  Octets a through b-1 inclusive of x (zero-based, half-open
      interval).

   [x, y, ...]:  An ordered list of octet strings.  A single-element
      list is written [x].  The brackets denote a list, not an optional
      parameter.

   frame, encode:  The length-prefixed injective encoding used by SEAL,

Sullivan                 Expires 14 January 2027               [Page 10]
Internet-Draft                    raAE                         July 2026

      defined in Section 4.3.  frame is total over all field lengths.
      The construction requires an injective framing (Section 5.5) but
      does not mandate this encoding.  Another profile MAY use any
      injective encoding that meets Section 5.5.

   Nk:  Key size in octets for the chosen AEAD algorithm.

   Nn:  Nonce size in octets for the chosen AEAD algorithm.

   Np:  Presented nonce size in octets: the per-segment nonce stored in
      metadata.  Np = Nn in random nonce mode and Np = 0 in derived
      nonce mode, where the nonce is recomputed from the key schedule.

   Nh:  Hash or pseudorandom function (PRF) output size in octets for
      the chosen KDF.

   Nt:  Authentication tag size in octets for the chosen AEAD algorithm.

   Na:  The snapshot value length in octets, the output size of the
      configured snapshot authenticator.  It MAY depend on the segment
      count n_seg, and is 0 when none (Section 4.7).

   n_seg:  The number of ciphertext segments in a stored message.
      Written n_seg (lowercase) to distinguish it from the per-message
      nonce N in the raAE interface (Section 3.2).

3.  The raAE Primitive

3.1.  Overview

   Random-access authenticated encryption (raAE) partitions a message
   into segments, each encrypted and decrypted on its own and in any
   order.  A reader can open one segment without the rest of the
   message, and a writer can encrypt one without holding the others.  In
   the base interface [FLRR25], each segment is written once and
   thereafter only read.

   The extended raAE interface keeps that base and adds one capability:
   a segment already written may be replaced in place, without
   disturbing the rest.  Rewriting changes the threat model.  A storage
   layer holding the ciphertext can roll a segment back to an older
   valid copy, substitute one valid same-index segment for another, or
   drop one, and per-segment authentication still passes on each segment
   it is shown.  An optional snapshot authenticates the segment set as
   the writer last recorded it, so a reader can detect these changes.
   The extension (Section 3.3) defines the snapshot and the operations
   that maintain it.

Sullivan                 Expires 14 January 2027               [Page 11]
Internet-Draft                    raAE                         July 2026

   The security notions follow the interface.  raAE and its rewrites
   target ra-ROR (random-access real-or-random) and ra-CMT (random-
   access context commitment) (Section 5.2).  Rewriting leaves both
   untouched, since replacing a segment is an encryption the read-only
   adversary could already ask for.  Snapshot authentication adds the
   one notion of its own, snapshot integrity (Section 5.2.3).

3.2.  Interface

   The base raAE interface, from [FLRR25], is a tuple of five
   algorithms: KeyGen, StartEnc, EncSeg, StartDec, and DecSeg.

   KeyGen:  Randomized key generation.  Takes no input.  Outputs a
      secret key K.

   StartEnc(K, N, G):  Deterministic.  Takes a secret key K, nonce N,
      and global associated data G.  Outputs a ciphertext header T_g and
      an initial per-message encryption state S.

   EncSeg(S, p, A_i, M_i):  Randomized.  Takes the encryption state S, a
      position identifier p, per-segment associated data A_i, and
      message segment M_i.  Outputs a ciphertext segment C_i.  S is not
      modified.  EncSeg may be called with the same S at any position,
      in any order, or concurrently.

   StartDec(K, N, G, T_g):  Deterministic.  Takes key K, nonce N, global
      associated data G, and ciphertext header T_g.  Outputs a per-
      message decryption state S or an error.

   DecSeg(S, p, A_i, C_i):  Deterministic.  Takes the decryption state
      S, position identifier p, per-segment associated data A_i, and
      ciphertext segment C_i.  Outputs message segment M_i or an error.

   The position identifier p = (i, b) consists of a segment number i and
   a terminal bit b in {0, 1}.  They impose a total ordering on segments
   and carry a truncation defense: a ciphertext lacking a segment with b
   = 1 is incomplete.  The encryption state S is immutable.  That
   immutability is what makes arbitrary-order and fully parallel
   operation possible.  The ciphertext header T_g commits to K, the
   nonce N, and global associated data G.  A headerless scheme emits T_g
   = empty.

3.3.  Extended Interface

   Replacing a segment already written, without re-encrypting the
   others, requires no new operation: re-encrypting a position is an
   ordinary EncSeg call over the immutable state, and it leaves ra-ROR
   and ra-CMT untouched.

Sullivan                 Expires 14 January 2027               [Page 12]
Internet-Draft                    raAE                         July 2026

   Rewriting gives a storage layer something to attack: it can roll a
   segment back to an older valid copy, substitute one valid same-index
   segment for another, or drop one while per-segment authentication
   still passes on what it presents.  A scheme that supports rewriting
   MAY carry a snapshot, a value over the current set of segments that a
   reader checks to detect these changes.  A write-once scheme MAY carry
   one too: a single value over the whole segment set gives a consuming
   protocol one thing to authenticate (Section 4.7.5).

   Two operations, added by this document, maintain and check the
   snapshot.  RewriteSeg replaces a segment and updates the snapshot in
   the same step.  SnapVerify decides whether a given set of segments is
   the one the writer last recorded.

   RewriteSeg(S, p, A_i, M'_i, C_i, snapshot):  Randomized.  Takes the
      encryption state S and position identifier p, per-segment
      associated data A_i, the new message segment M'_i, the existing
      ciphertext C_i at position p, and the current snapshot.  Outputs
      replacement ciphertext C'_i and an updated snapshot value,
      snapshot'.

   SnapVerify(S, segments, snapshot):  Deterministic.  Takes the
      encryption state S, the present segments (the (position,
      ciphertext segment) pairs of the current object state), and the
      snapshot.  Outputs accept or reject.

   The extension also changes two base signatures.  In the extended
   interface:

   StartEnc(K, N, G):  Deterministic.  As in Section 3.2, and
      additionally outputs the initial snapshot value, the snapshot over
      the empty segment set.

   EncSeg(S, p, A_i, M_i):  Randomized.  As in Section 3.2, and
      additionally outputs the snapshot value over the segment set that
      results from adding C_i at position p.

   The snapshot is an authenticator over the set of segments belonging
   to the current object state.  When the scheme supports incremental
   update, an update can run in time independent of the total segment
   count.  This is an implementation goal, not a syntactic requirement
   of the interface.  The base primitive has no snapshot value.  The
   snapshot, the two signature changes above, RewriteSeg, and SnapVerify
   are defined by this document.

   These additions define the extended raAE interface of this document:
   the tuple (KeyGen, StartEnc, EncSeg, StartDec, DecSeg, RewriteSeg,
   SnapVerify) over the auxiliary snapshot value.  The first five

Sullivan                 Expires 14 January 2027               [Page 13]
Internet-Draft                    raAE                         July 2026

   algorithms are those of Section 3.2, with the snapshot outputs of
   StartEnc and EncSeg defined above.  The snapshot value, RewriteSeg,
   and SnapVerify are the extension.  A scheme can realize the base
   interface alone, as FLOE ([FLRR25]) does.  SEAL (Section 4) realizes
   the full extended interface.  Length change adds no further
   algorithm, as the next paragraph describes.

   Changing a message's length is a composition of these operations, not
   a separate algorithm in either direction.  Position-addressed
   encryption over immutable state already permits re-encrypting a
   position or encrypting at a higher index (see Section 5.2.1).

   A holder appends by encrypting the new segments and re-marking the
   old final segment with RewriteSeg to clear its terminal bit.  It
   truncates by re-marking the new final segment with RewriteSeg,
   dropping the trailing segments, and updating the snapshot over the
   surviving set.  Appending to an empty object skips the re-mark, since
   there is no old final segment, and truncating away every segment
   yields the empty object, with no new final segment to re-mark.

   Length changes occur only at the end of the message: a holder may
   append segments or drop a suffix, but cannot remove or renumber front
   or interior segments, because positions are index-addressed.  Each
   step updates the snapshot, and Section 4.9.2.2 gives the construction
   for both directions.

4.  SEAL: A Concrete raAE Construction

   SEAL is a parameterized construction for realizing the raAE primitive
   defined in Section 3.  A SEAL instantiation combines an AEAD, a KDF,
   a key schedule, a nonce-generation method, a commitment mechanism,
   and an optional snapshot authenticator to produce an raAE scheme for
   segmented stored content.

   The raAE interface defines what operations and security properties a
   scheme provides.  SEAL defines how those operations are realized from
   standard cryptographic components.  Different SEAL instantiations MAY
   select different AEADs, KDFs, nonce modes, commitment mechanisms, or
   snapshot authenticators, provided they satisfy the requirements of
   this section.

   The remainder of this section specifies the common SEAL construction.
   Section 5 analyzes its security, and concrete parameter choices and
   interoperable suites are in Section 4.10.

Sullivan                 Expires 14 January 2027               [Page 14]
Internet-Draft                    raAE                         July 2026

   The key hierarchy is shown below.  A CEK and per-content salt feed
   the payload schedule, which derives four values.  The payload key
   feeds per-segment keys (optionally through epoch keys), and each
   segment produces an AEAD tag.  When a snapshot authenticator is
   configured, every segment feeds it to produce the snapshot.

                     CEK + salt
                         |
                         v
                  .-----------.
                  |  Payload  |
                  |  Schedule |
                  '-----------'
               /     |      |     \
              v      v      v      v
          commit  payload  nonce   snap
                   key     base    key
            |       |       |       |
            v       v       v       |
          .---.  .------. .-----.   |
          |cmt|  |seg   | |nonce|   |
          |   |  |key(i)| | (i) |   |
          '---'  '------' '-----'   |
                     \       /      |
                      \     /       |
                       v   v        |
                    .----------.    |
                    |   AEAD   |    |
                    |  Seal(i) |    |
                    '----------'    |
                      /      \      |
                     v        v     |
                   ct_i     tag_i   |
                               \    |
                                v   v
                        .-----------------.
                        |    Snapshot     |
                        |  Authenticator  |
                        '-----------------'
                                 |
                                 v
                             snapshot

                        Figure 2: SEAL Key Hierarchy

Sullivan                 Expires 14 January 2027               [Page 15]
Internet-Draft                    raAE                         July 2026

   The figure shows the masked multiset hash, whose per-segment input is
   the AEAD tag.  Under the digest transcript (snap_id 0x0002) the
   snapshot authenticator instead takes each segment's leaf leaf(i) =
   LH(ct_i) || tag(i) (Section 4.7.5).

4.1.  Construction Overview

   A commitment lets a reader reject a wrong key or parameter set before
   decrypting anything.  An optional snapshot authenticates the current
   set of segments.

   SEAL realizes the raAE operations of Section 3.2 as follows:

         +============+==========================================+
         | raAE       | SEAL realization                         |
         | operation  |                                          |
         +============+==========================================+
         | StartEnc   | Derive the payload schedule from the CEK |
         |            | and salt, committing G (Section 4.5)     |
         +------------+------------------------------------------+
         | StartDec   | Re-derive the schedule and verify the    |
         |            | stored commitment (Section 4.6)          |
         +------------+------------------------------------------+
         | EncSeg,    | EncryptSegment and DecryptSegment        |
         | DecSeg     | (Section 4.8)                            |
         +------------+------------------------------------------+
         | RewriteSeg | RewriteSegment, then update the snapshot |
         |            | authenticator for the changed segment    |
         |            | (Section 4.9.2.1)                        |
         +------------+------------------------------------------+
         | SnapVerify | Check the present indices and finality,  |
         |            | then verify the snapshot via the         |
         |            | authenticator (Section 4.7)              |
         +------------+------------------------------------------+

            Table 1: raAE operations and their SEAL realizations

   SEAL's salt, payload_info, and G carry the raAE per-message inputs,
   and the commitment stands in for the header (Section 4.2).

4.2.  Component Suite and Parameters

   This section names every parameter, input, and derived symbol the
   construction uses.  Table 2 summarizes them, grouped by what sets
   each one.  The entries that follow define the suite choices, the
   profile-level constants, the algorithm-determined sizes, and the per-
   message inputs.  Derived values are defined where they are computed,
   in the sections the table cites.

Sullivan                 Expires 14 January 2027               [Page 16]
Internet-Draft                    raAE                         July 2026

    +===================+===================+=============+==========+
    | Parameter         | Type or range     | Set by      | Defined  |
    |                   |                   |             | in       |
    +===================+===================+=============+==========+
    | AEAD              | RFC 5116          | suite       | Section  |
    |                   | algorithm         |             | 4.10.1   |
    +-------------------+-------------------+-------------+----------+
    | KDF               | kdf_id from       | suite       | Section  |
    |                   | Table 11          |             | 4.3      |
    +-------------------+-------------------+-------------+----------+
    | segment_max       | power of two, at  | suite       | Section  |
    |                   | least 4096        |             | 4.10     |
    +-------------------+-------------------+-------------+----------+
    | epoch_length      | integer r in 0 to | suite       | Section  |
    |                   | 63                |             | 4.5.2    |
    +-------------------+-------------------+-------------+----------+
    | protocol_id       | octet string      | application | Section  |
    |                   |                   |             | 6.11     |
    +-------------------+-------------------+-------------+----------+
    | nonce_mode        | "random" or       | message,    | Section  |
    |                   | "derived"         | within the  | 4.5.3    |
    |                   |                   | profile     |          |
    +-------------------+-------------------+-------------+----------+
    | aad_label         | ASCII string      | profile     | Section  |
    |                   | ("SEAL-DATA" in   |             | 4.4.2    |
    |                   | SEAL)             |             |          |
    +-------------------+-------------------+-------------+----------+
    | commitment_length | integer, at least | profile     | Appendix |
    |                   | 16, default Nh    |             | C.4      |
    +-------------------+-------------------+-------------+----------+
    | Nk, Nn, Nt        | sizes in octets   | AEAD        | Section  |
    |                   |                   |             | 2.2      |
    +-------------------+-------------------+-------------+----------+
    | Nh                | size in octets    | KDF         | Section  |
    |                   |                   |             | 2.2      |
    +-------------------+-------------------+-------------+----------+
    | CEK               | 32 uniform random | message     | Section  |
    |                   | octets            | input       | 4.5      |
    +-------------------+-------------------+-------------+----------+
    | salt              | 32 uniform random | message     | Section  |
    |                   | octets            | input       | 4.5      |
    +-------------------+-------------------+-------------+----------+
    | payload_info      | ordered element   | message     | Section  |
    |                   | list              | input       | 4.4      |
    +-------------------+-------------------+-------------+----------+
    | G                 | octet string,     | message     | Section  |
    |                   | empty by default  | input       | 4.6      |
    +-------------------+-------------------+-------------+----------+

Sullivan                 Expires 14 January 2027               [Page 17]
Internet-Draft                    raAE                         July 2026

    | commitment        | commitment_length | derived per | Section  |
    |                   | octets            | message     | 4.5      |
    +-------------------+-------------------+-------------+----------+
    | payload_key       | Nk octets         | derived per | Section  |
    |                   |                   | message     | 4.5      |
    +-------------------+-------------------+-------------+----------+
    | snap_key          | Nh octets         | derived per | Section  |
    |                   |                   | message     | 4.5      |
    +-------------------+-------------------+-------------+----------+
    | nonce_base        | Nn octets,        | derived per | Section  |
    |                   | derived mode only | message     | 4.5      |
    +-------------------+-------------------+-------------+----------+
    | epoch_key(i)      | Nk octets         | derived per | Section  |
    |                   |                   | segment     | 4.5.2    |
    +-------------------+-------------------+-------------+----------+
    | segment_key(i)    | Nk octets         | derived per | Section  |
    |                   |                   | segment     | 4.5.2    |
    +-------------------+-------------------+-------------+----------+
    | nonce(i)          | Nn octets         | derived per | Section  |
    |                   |                   | segment     | 4.5.3    |
    +-------------------+-------------------+-------------+----------+
    | segment_aad(i,    | octet string      | derived per | Section  |
    | is_final, A_i)    |                   | segment     | 4.4.2    |
    +-------------------+-------------------+-------------+----------+
    | tag(i)            | Nt octets         | derived per | Section  |
    |                   |                   | segment     | 4.8      |
    +-------------------+-------------------+-------------+----------+

             Table 2: Parameters, inputs, and derived values

   SEAL fixes the segment additional authenticated data (AAD) encoding,
   the commitment derivation, and the snapshot value for every suite.
   They are specified in Section 4.4.2, Section 4.6, and Section 4.7
   respectively, and are encoded identically across all SEAL cipher
   suites.  The resulting octet lengths still vary by suite, because Nh
   and Nt are suite-dependent.

4.2.1.  Suite Choices

   A suite is assembled from the following choices.

   AEAD:  An authenticated encryption with associated data algorithm
      satisfying the interface of [RFC5116], with Nk-octet keys, Nn-
      octet nonces, and Nt-octet authentication tags.

   KDF:  A key derivation function supporting the abstract interface
      defined in Section 4.3.  See Table 11 for the permitted KDFs.

Sullivan                 Expires 14 January 2027               [Page 18]
Internet-Draft                    raAE                         July 2026

   segment_max:  A positive integer, a power of two and at least 4096
      octets.  Each segment carries at most this many plaintext octets.
      The length-dependent limits of Section 5.9 are computed at this
      size.

   epoch_length:  A non-negative integer r in 0 to 63.  Each epoch
      covers 2^r consecutive segments aligned to a multiple of 2^r
      (Section 4.5.2).  Profiles MAY restrict r further per AEAD
      (Section 4.10).  The value sets a rotation granularity, not an on/
      off switch: r = 0 selects the finest rotation, a fresh epoch key
      for every segment, and no value of r disables epoch-key
      derivation.  A flat key, one epoch key covering all segments, is r
      = 63.

   protocol_id:  An application-chosen octet string that binds all
      derived values to a specific protocol version.  Selection guidance
      is in Section 6.11.

   nonce_mode:  A payload_info element selecting one of two nonce
      constructions, "random" or "derived".  The constructions are
      defined in Section 4.5.3, the default mode each SEAL suite uses in
      the mutable profile (Table 10), and the valid (nonce_mode,
      snap_id) tuples for each profile in Section 4.10.2.

4.2.2.  Profile-Level Constants

   One constant is fixed by the profile rather than carried per message.

   aad_label:  A profile-level ASCII string bound as the first element
      of the segment AAD encoding (Section 4.4.2).  Consuming protocols
      MAY define their own profiles with a distinct aad_label.

4.2.3.  Algorithm-Determined Sizes

   The sizes Nk, Nn, Nt, and Nh are fixed by the chosen AEAD and KDF
   rather than by the parameter set directly.  They are defined in
   Section 2.2, with concrete values in Table 10 and Table 11.  One
   configurable length defaults from Nh.

   commitment_length:  A positive integer of at least 16, defaulting to
      Nh, the length in octets of the key commitment prefix.  The
      collision bounds for each length are given in Appendix C.4.

4.2.4.  Per-Message Inputs

   An encrypted message is built from four input values.

   CEK:  A 32-octet uniform random Content Encryption Key generated

Sullivan                 Expires 14 January 2027               [Page 19]
Internet-Draft                    raAE                         July 2026

      fresh for each message.  The CEK is input keying material for the
      key schedule, not an AEAD key.

   salt:  A per-content value of 32 uniformly random octets.  The salt
      separates payload schedule outputs across messages even when the
      CEK is reused.  Section 4.9.1.1 and Section 4.9.2.2 give the rules
      for generating and reusing it, and Section 6.7 analyzes the
      consequences of reuse.

   payload_info:  The committed record of the object's configuration: an
      ordered list of parameter set context elements covering key
      derivation, AEAD operations, AAD construction, and nonce
      construction, which the commitment (Section 4.6) binds.  The
      profile fixes the concrete element set and their encodings.
      SEAL's payload_info is specified in Section 4.4.

   G:  An octet string of global associated data, the G input of
      StartEnc in the raAE primitive (Section 3.2), binding whole-
      message application context such as a name, version, or policy.  G
      defaults to the empty octet string and is never stored: a
      decryptor supplies it from application context, and the commitment
      binds it (Section 4.6).

   The remaining symbols in Table 2 are derived values.  The payload
   schedule (Section 4.5) computes the per-message values commitment,
   payload_key, snap_key, and nonce_base once from the CEK and
   payload_info (the commitment also binds G, Section 4.6), and they are
   fixed for the message's lifetime.  The per-segment values
   epoch_key(i), segment_key(i), nonce(i), segment_aad(i, is_final,
   A_i), and tag(i) are defined where they are computed: Section 4.5.2,
   Section 4.5.3, Section 4.4.2, and Section 4.8.

4.3.  The KDF Combiner

   The KDF combiner uses a length-prefixed encoding for all of its
   inputs.

   frame(x):
       if len(x) <= 0xFFFE:  return uint16(len(x)) || x     ;; literal
       else:                 return uint16(0xFFFF) || LH(x) ;; digest

   encode(x1, ..., xn) = frame(x1) || ... || frame(xn)

Sullivan                 Expires 14 January 2027               [Page 20]
Internet-Draft                    raAE                         July 2026

   frame is total over every field length.  A field of at most 0xFFFE
   octets is emitted literally behind its 2-octet length, identical to a
   plain length prefix.  The reserved length 0xFFFF is a typed prefix
   meaning that an Nh-octet digest LH(x) of an over-large field follows
   in place of the field itself.

   LH(x), the over-large-field digest, runs the cipher suite's native
   KDF primitive directly on x, invoking neither encode nor frame, and
   returns Nh octets.  The two-step and one-step classes are HPKE's: a
   two-step KDF ([RFC9180]) uses its extract step, and a one-step KDF
   ([I-D.ietf-hpke-pq]) its XOF, with x carried under the label "raAE-
   LP-v1":

   two-step:  LH(x) = Extract("raAE-LP-v1", x)
   one-step:  LH(x) = XOF("raAE-LP-v1" || x, Nh)

   Routing LH through the KDF would encode-frame x and recurse, because
   frame of an over-large x would re-enter LH.  LH therefore runs the
   native primitive directly on x.  The injectivity of encode and the
   domain-separation and collision properties of LH are established in
   Section 5.5.

   Key material is derived through a KDF with the interface

   KDF(protocol_id, label, ikm, info, L)

   *  protocol_id: binds the output to a specific protocol version and
      application context.

   *  label: a unique ASCII string identifying the derivation role.

   *  ikm: input keying material, provided as a single octet string or
      an ordered list of octet strings.

   *  info: context information, provided as a single octet string or an
      ordered list of octet strings.

   *  L: the requested output length in octets.

   This KDF binds protocol_id, a label, and inputs of any size into a
   derived value.  frame reduces any over-large input to a fixed-size
   digest before the KDF sees it.  Any protocol instantiates the
   combiner by supplying its own protocol_id, which domain-separates its
   derivations from those of every other protocol.  SEAL's two profiles
   instantiate the combiner at protocol_id SEAL-RW-v1 and SEAL-RO-v1
   (Section 4.10.2).

Sullivan                 Expires 14 January 2027               [Page 21]
Internet-Draft                    raAE                         July 2026

   The KDF is built from the cipher suite's hash in one of two forms.
   The cipher suite's kdf_id (Table 11) selects which one.

4.3.1.  Two-Step KDF

   KDF(protocol_id, label, ikm, info, L):
     extract_input = encode(protocol_id, label,
                            ...ikm)
     prk = Extract(salt=protocol_id,
                   ikm=extract_input)
     expand_info = encode(protocol_id, label,
                          ...info, uint16(L))
     return Expand(prk, expand_info, L)

   The notation ...x means each element of the sequence x, whether ikm
   or info, is a separate argument to encode.  A single octet string is
   the one-element sequence containing it.  An absent value is the empty
   sequence and contributes no arguments.  The empty sequence and the
   one-element sequence whose element is the empty octet string are
   distinct: the first adds nothing to the encode, the second adds one
   zero-length field.  A derivation with no info MUST use the empty
   sequence, not the empty octet string, so that every implementation
   encodes a given (protocol_id, label, ikm, info, L) tuple to the same
   octets.  The protocol_id appears in both the Extract salt and the
   extract_input as a defensive measure, and binding in either position
   suffices for domain separation.

   The label appears in both Extract and Expand so that label binding is
   preserved even if one phase is weak in isolation.  Implementations
   MAY amortize the Extract computation internally when deriving
   multiple outputs from the same protocol_id, label, and ikm.

4.3.2.  One-Step KDF

   KDF(protocol_id, label, ikm, info, L):
     M = encode(protocol_id, label,
                encode(...ikm), encode(...info),
                uint16(L))
     return XOF(M, L)

   The XOF-based form frames ikm and info each as a single element of
   one length-prefixed encode and squeezes L octets of output.
   protocol_id binding is via the message-side encode prefix only.  In
   both forms the encoded input ends with uint16(L): the two-step form
   places it last in expand_info and the one-step form places it last in
   M.  Within a fixed kdf_id, distinct (protocol_id, label, ikm, info,
   L) tuples yield distinct outputs, by the injectivity of encode
   (established in Section 5.5 and demonstrated in Appendix F.3).  The

Sullivan                 Expires 14 January 2027               [Page 22]
Internet-Draft                    raAE                         July 2026

   two forms are not mutually injective: a cipher suite's kdf_id
   (Table 11) selects one form for that suite, and payload_info commits
   to the kdf_id (Section 4.4), so no derivation ever compares outputs
   across forms.  The two-step form binds ikm in Extract and info in
   Expand.  A multi-value ikm or info is the encode of its elements, so
   any number of elements is admitted.

   The following example expands M for combiner vector KDF.33
   (Appendix F.3): TurboSHAKE-256, label "commit", ikm a single 32-octet
   element of repeated 0xAA, info the two elements 010203 and 0405, and
   L = 64.  Each frame is a 2-octet length followed by the element, and
   the ikm and info lists are each one nested encode:

   M, 71 octets, in frame order:

   000a 5345414c2d52572d7631      frame(protocol_id "SEAL-RW-v1")
   0006 636f6d6d6974              frame(label "commit")
   0022 0020 aa..aa (32 octets)   frame(encode(...ikm)): one inner
                                  frame wrapping the single element
   0009 0003 010203 0002 0405     frame(encode(...info)): inner
                                  frames of 3 and 2 octets
   0002 0040                      frame(uint16(L)), L = 64

   XOF(M, 64) = the KDF.33 output

   With info = [], the empty sequence, the fourth frame is the two
   octets 0000: encode() of no elements is the empty octet string, and
   its frame is a zero length with no content.  With info = [""], one
   zero-length element, the inner encode is 0000 and the fourth frame is
   0002 0000.  KDF.8 and KDF.28 (Appendix F.3) exercise the same
   distinction in the two-step form.

4.4.  SEAL Encodings and Labels

   SEAL fixes the concrete encodings the combiner and the AEAD consume:
   the payload_info tuple, the segment AAD, and the derivation labels.

4.4.1.  Payload Info Construction

   payload_info = [aead_id, segment_max_be, kdf_id, snap_id,
                   nonce_mode, epoch_length_u8, salt]

   Element encodings for SEAL:

   *  aead_id: 2 octets, uint16(id), the unsigned 16-bit AEAD identifier
      from Table 10 (for example, 0x0002 for AES-256-GCM).

Sullivan                 Expires 14 January 2027               [Page 23]
Internet-Draft                    raAE                         July 2026

   *  segment_max_be: 4 octets, uint32(segment_max).  SEAL's segment
      sizes are given in Section 4.10.

   *  kdf_id: 2 octets, uint16(id), the unsigned 16-bit KDF identifier
      from Table 11 (0x0001 for HKDF-SHA-256 per [RFC9180] Section 7.2;
      0x0013 for TurboSHAKE-256 per [I-D.ietf-hpke-pq]).

   *  snap_id: 2 octets, uint16(id), the snapshot authenticator
      identifier from Table 12 (0x0000 for none, 0x0001 for the masked
      multiset hash, 0x0002 for the digest transcript, 0x0003 for the
      epoch digest tree).

   *  nonce_mode: 1 octet, the nonce construction (Table 13): 0x00 for
      random, 0x01 for derived.  Table 10 gives the default mode each
      SEAL suite uses in the mutable profile.  A profile MAY select
      another valid (nonce_mode, snap_id) tuple.

   *  epoch_length_u8: 1 octet, uint8(epoch_length).  Valid values per
      Table 10.  Implementations MUST reject epoch_length >= 64 on both
      encode and decode.

   *  salt: 32 raw octets, the per-message salt.

   The aad_label is a profile-level constant for SEAL (see
   Section 4.4.2) and is not carried in payload_info.  It is bound
   transitively via protocol_id.  The KDF (Section 4.3) applies frame to
   each element of the list.

4.4.2.  Segment AAD

   The segment AAD binds each segment to its context.  What it must
   carry depends on the nonce mode: a random nonce binds neither the
   segment index nor the finality bit, so the AAD must, whereas a
   derived nonce binds both (Section 4.5.3.2).

   segment_aad(i, is_final, A_i):
     if nonce_mode is "random":
       if A_i is empty:
         return encode(aad_label, uint64(i), uint8(is_final))
       return encode(aad_label, uint64(i), uint8(is_final), A_i)
     ;; derived nonce mode: index and is_final are bound in the nonce
     if A_i is empty:
       return ""              ;; no AEAD associated-data pass
     return encode(aad_label, A_i)

Sullivan                 Expires 14 January 2027               [Page 24]
Internet-Draft                    raAE                         July 2026

    +=============+======================+============================+
    |             | Random nonce mode    | Derived nonce mode         |
    +=============+======================+============================+
    | Index i and | bound in the AAD     | bound in the nonce         |
    | is_final    |                      |                            |
    +-------------+----------------------+----------------------------+
    | A_i, when   | appended as a fourth | the sole variable element  |
    | present     | encode element       |                            |
    +-------------+----------------------+----------------------------+
    | A_i, when   | omitted, not a zero- | segment_aad is empty, with |
    | empty       | length element       | no associated-data pass    |
    +-------------+----------------------+----------------------------+

                     Table 3: Segment AAD by nonce mode

   aad_label is the profile-chosen label from the parameter set, the
   index i is a big-endian 8-octet integer, and is_final is 1 for the
   last segment and 0 for all others.  Because encode length-prefixes
   every element, omitting an empty A_i stays injective, so the index
   and finality bit remain authenticated.  Any profile that defines a
   random-mode segment_aad MUST preserve injectivity and MUST
   authenticate the segment index and finality bit.  aad_label is bound
   transitively through protocol_id (Section 6.3), so segment_aad need
   not repeat it for cross-profile separation.

   More generally, the binding of the triple (i, is_final, A_i) MUST be
   unambiguous under one segment key, so that no two distinct triples
   present the same inputs to the AEAD.  In random nonce mode
   segment_aad alone MUST injectively bind the triple, because the nonce
   is an independent random draw.  In derived nonce mode i and is_final
   are bound in the nonce and A_i, when present, in segment_aad, and the
   (nonce, segment_aad) pair MUST jointly determine the triple
   (Section 4.5.3.2).

   A_i is the caller-supplied per-segment associated data of EncSeg and
   DecSeg (Section 3.2), passed to the segment algorithms (Section 4.8).
   It MAY be empty and MAY differ per segment.  Unlike a global
   associated data value G, which the commitment fixes at creation
   (Section 4.6), A_i rides the per-segment AEAD associated data and is
   rewritable: a rewrite can change a segment's A_i.  The example test
   vectors use an empty A_i, so the random-mode vectors keep the three-
   element encoding and the derived-mode vector has an empty
   segment_aad.

Sullivan                 Expires 14 January 2027               [Page 25]
Internet-Draft                    raAE                         July 2026

   The salt is not included in the AAD because it is already bound into
   segment_key via the payload schedule.  Different messages produce
   different segment keys even at the same index.  Transposing two
   segments within a message fails because the AAD at each position
   encodes the expected index.

   Unauthorized truncation is detectable, and Section 4.9.1.2 requires a
   decryptor to reject it.  A reader that sees the highest-indexed
   present segment carry is_final = 0 knows more data must follow.
   Authorized truncation by the CEK holder instead re-marks the new
   final segment and updates the snapshot (Section 4.9.2.2).  Extension
   is also caught: appending after a segment marked is_final = 1 either
   invalidates the original final segment on re-read or produces a
   ciphertext that cannot verify.

   Each segment binds both its index and its finality bit, through the
   AAD in random nonce mode and through the nonce in derived nonce mode.
   This per-segment binding, rather than reliance on the snapshot alone,
   catches these attacks.  A reader can check them without holding all
   tags before decrypting any one segment.

   The per-segment AEAD binds a segment's index and finality, not its
   length (through the AAD in random nonce mode and through the nonce in
   derived nonce mode).  A segment's ciphertext length is authenticated
   implicitly by the AEAD tag over the ciphertext: altering, truncating,
   or splicing a segment ciphertext is an AEAD forgery, not a parsing
   ambiguity.  Because segments MAY be shorter than segment_max, the
   consuming format MUST convey each segment's ciphertext length to the
   decoder.  A wrong length yields an AEAD rejection, never a silent
   accept.

4.4.3.  Labels

    +============================+===================+===============+
    | Derivation role            | Label variable    | Value         |
    +============================+===================+===============+
    | Commitment                 | commit_label      | "commit"      |
    +----------------------------+-------------------+---------------+
    | Payload key                | payload_key_label | "payload_key" |
    +----------------------------+-------------------+---------------+
    | Snapshot authenticator key | snap_key_label    | "acc_key"     |
    +----------------------------+-------------------+---------------+
    | Nonce base                 | nonce_base_label  | "nonce_base"  |
    +----------------------------+-------------------+---------------+
    | Epoch key                  | epoch_key_label   | "epoch_key"   |
    +----------------------------+-------------------+---------------+

                  Table 4: SEAL core KDF labels by role

Sullivan                 Expires 14 January 2027               [Page 26]
Internet-Draft                    raAE                         July 2026

   SEAL fixes each label to the value above.  The construction formulas
   reference labels by variable name, so the labels are SEAL parameters
   rather than constants of the raAE construction.  Each value is an
   ASCII string and is distinct from all others.  These label strings
   are frozen for wire compatibility.  In particular the snapshot
   authenticator key snap_key uses the label string "acc_key", retained
   from an earlier name.  These are the always-present key-schedule
   labels.  The masked multiset hash (Section 4.7.4), the digest
   transcript (Section 4.7.5), the plaintext-bound nonce construction
   (Appendix A), and hedged randomness (Appendix B) each define their
   own labels.

4.5.  Key Schedule and Nonce Generation

   A message's entire key hierarchy grows from two random values: the
   32-octet CEK and the per-content salt.  All other keys are derived
   deterministically through the KDF of Section 4.3.

4.5.1.  Payload Schedule

   Four values are derived from the CEK and per-content salt.  The
   commitment allows early rejection of a wrong key and additionally
   binds the global associated data G (Section 4.2.4).  The payload key
   encrypts segments through epoch keys.  The snapshot authenticator key
   is used by the configured snapshot authenticator.  The nonce base
   (derived-mode only) seeds deterministic per-segment nonces.

   payload_info = [aead_id,
                   uint32(segment_max),    ;; segment_max_be
                   kdf_id,
                   snap_id,
                   nonce_mode,
                   uint8(epoch_length),    ;; epoch_length_u8
                   salt]

   commitment  = KDF(protocol_id, commit_label,
                     [CEK], [...payload_info, G], commitment_length)
   payload_key = KDF(protocol_id, payload_key_label,
                     [CEK], payload_info, Nk)
   snap_key     = KDF(protocol_id, snap_key_label,
                     [CEK], payload_info, Nh)

   ;; For derived nonce mode only:
   nonce_base  = KDF(protocol_id, nonce_base_label,
                     [CEK], payload_info, Nn)

Sullivan                 Expires 14 January 2027               [Page 27]
Internet-Draft                    raAE                         July 2026

   G is the global associated data of Section 4.2.4, always the last
   element of the commitment info.  It defaults to the empty octet
   string, one zero-length element, so every commitment derivation
   includes it.  The other three derivations never take G.

4.5.2.  Epoch Key Derivation

   AES-128-GCM, AES-256-GCM, and ChaCha20-Poly1305 use a 96-bit nonce.
   At a 2^(-32) collision-probability target, the random-nonce budget is
   about 2^32 encryptions per key.  For rewritable content, this limit
   can be reached through repeated modifications to the same segments.
   Epoch keys partition the nonce space: each epoch key covers at most
   2^r segment positions (initial writes plus rewrites), so the per-key
   invocation count is bounded regardless of the content's total size.

   When epoch_length = r is specified, the segment key for segment i is
   an epoch key derived from the payload key:

   segment_key(i):
     epoch_index = i >> epoch_length
     return KDF(protocol_id, epoch_key_label,
                [payload_key],
                [uint64(epoch_index)], Nk)

   At epoch_length = 0 the shift is the identity, so each segment has
   its own epoch key.  At a large epoch_length every segment shares one
   epoch key.  Implementations MUST apply this derivation at every
   epoch_length, including 0, and MUST NOT use payload_key directly as a
   segment key.  Epoch keys implement the parallel external rekeying
   pattern of [RFC8645], adapted for random-access patterns.

4.5.3.  Nonce Generation

   Two nonce modes, random and derived, trade off AEAD flexibility,
   storage cost, and trust in a cryptographically secure pseudorandom
   number generator (CSPRNG).  Derived mode needs misuse-resistant
   authenticated encryption (MRAE) unless the profile is write-once
   (Section 4.10.2).  The chosen mode is part of the parameter set and
   MUST be consistent across all segments of a message.

   Random:            Derived:
                      nonce_base
   Random(Nn) -.        |
               |      XOR ((i<<1)|is_final)
               v        |
             nonce_i  nonce_i

                       Figure 3: The Two Nonce Modes

Sullivan                 Expires 14 January 2027               [Page 28]
Internet-Draft                    raAE                         July 2026

   The two modes differ as follows:

     +===============+===================+===========================+
     | Property      | Random nonce mode | Derived nonce mode        |
     +===============+===================+===========================+
     | Per-segment   | yes, in segment   | no                        |
     | nonce stored  | metadata          |                           |
     +---------------+-------------------+---------------------------+
     | CSPRNG        | yes               | no                        |
     | required      |                   |                           |
     +---------------+-------------------+---------------------------+
     | AEAD          | any AEAD          | MRAE, or any in a write-  |
     | requirement   |                   | once profile              |
     +---------------+-------------------+---------------------------+
     | Rewrite       | fresh nonce each  | nonce fixed by index,     |
     |               | write             | reused on rewrite         |
     +---------------+-------------------+---------------------------+
     | Per-segment   | binds index and   | empty when no A_i (index/ |
     | AAD           | finality          | finality in nonce)        |
     +---------------+-------------------+---------------------------+
     | Minimum nonce | no added          | at least 8 octets         |
     | size Nn       | constraint        |                           |
     +---------------+-------------------+---------------------------+

                  Table 5: Random and derived nonce modes

4.5.3.1.  Random Nonce Mode

   In random nonce mode (nonce_mode = "random"), each segment gets an
   independent random nonce:

   nonce(i) = Random(Nn)

   Each segment's nonce is stored in per-segment metadata and MUST be
   accessible to any reader that decrypts individual segments.  This
   mode requires a functioning CSPRNG.  The nonce collision probability
   across independently generated per-segment nonces follows the
   standard birthday bound.  See Section 5.9.

   Two optional encryptor-side hedges harden this mode against different
   threats.  Hedged randomness (Appendix B) mixes in a long-term key and
   defends against a weak or predictable CSPRNG.  It does not defend
   against CSPRNG state duplication, since identical CSPRNG output still
   yields identical hedged output.  Only the plaintext-bound
   construction (Appendix A), which mixes in plaintext content, defends
   against state duplication.  Both are implementation-local, leave the
   wire format unchanged, and are not separate nonce modes.

Sullivan                 Expires 14 January 2027               [Page 29]
Internet-Draft                    raAE                         July 2026

4.5.3.2.  Derived Nonce Mode

   Derived nonce mode is intended for profiles that do not store a per-
   segment nonce.  Nonces are derived deterministically from the payload
   schedule.  Derived nonce mode is defined only for AEADs whose nonce
   size Nn is at least 8 octets.  The segment index occupies the high
   bits of an 8-octet counter and the finality bit occupies the low bit,
   so the per-segment AAD need not carry either one.  The XOR is applied
   to the last 8 octets of nonce_base, with the remaining octets
   unchanged:

   nonce(i) = nonce_base[0:Nn-8]
              || (nonce_base[Nn-8:Nn]
                  XOR uint64((i << 1) | is_final))

   No per-segment nonce storage is required.

   The encoded value (i << 1) | is_final MUST fit in the low 8 octets of
   nonce_base, so the segment index i MUST be less than 2^63.  This caps
   an object at 2^63 segments, far beyond any reachable object.  See
   Section 5.9.4 for the resulting size limit.

   A derived nonce is fixed by the segment index, so re-encrypting a
   segment reuses its nonce.  An MRAE AEAD such as AES-256-GCM-SIV
   ([RFC8452]) tolerates that reuse, degrading to equality leakage
   rather than plaintext recovery, within the per-segment rewrite limit
   given in Section 5.9.  A non-MRAE AEAD does not: reusing its nonce
   leaks plaintext, so with a non-MRAE AEAD, derived nonce mode MUST be
   confined to a write-once profile, in which each segment is encrypted
   exactly once.

   A write-once profile rewrites nothing and performs no length change.
   A rewrite would repeat a derived nonce, and a length change re-marks
   the terminal segment under a fresh nonce (is_final flips the low
   bit).  The write-once discipline is the simplest rule that keeps
   every derived nonce unique.  It MUST draw a fresh salt for every
   object, so the nonce base differs, and MUST NOT re-encrypt under that
   salt after a crash (Section 6.2).

Sullivan                 Expires 14 January 2027               [Page 30]
Internet-Draft                    raAE                         July 2026

   Folding is_final into the nonce also removes a per-segment cost.  The
   nonce binds the index and finality, so the per-segment associated
   data need not carry them: segment_aad is empty when the caller
   supplies no A_i, and is encode(aad_label, A_i) otherwise
   (Section 4.4.2).  When A_i is empty the AEAD processes no associated
   data, removing the per-segment associated-data pass that random nonce
   mode incurs.  This is the structural reason an immutable, write-once
   profile in derived nonce mode coincides with STREAM (Section 1.1) at
   the nonce-and-AEAD layer: a key-derived counter-plus-final-bit nonce
   over a per-segment AEAD with no associated data.

4.6.  Commitment

   Per-segment AEAD authenticates the ciphertext core together with the
   segment index and finality bit (through the segment AAD in random
   nonce mode and through the nonce in derived nonce mode) so position
   binding for the segment being opened is already provided.  Two
   further properties remain: context commitment, provided here, and
   snapshot membership, provided by the snapshot authenticator
   (Section 4.7).  The commitment lets a reader reject an incorrect CEK
   or parameter context before attempting any segment decryption,
   providing ra-CMT-style context commitment (Section 5.2.2) for the
   construction.

   The commitment is a commitment_length-octet value the payload
   schedule derives from the CEK, payload_info, and the global
   associated data G (Section 4.5).

   Before decrypting any segment, a reader derives the expected
   commitment from the CEK, payload_info, and G, and compares it octet-
   for-octet with the stored value.  A reader MUST verify the commitment
   before decrypting any segment and MUST treat a mismatch as an
   authentication failure for the object.  A mismatch indicates a wrong
   key, a wrong parameter set, a wrong G, or a corrupted header.  This
   check provides ra-CMT context commitment.  The reduction outline and
   bound are in Section 5.7.

   G is the global associated data input of StartEnc in the raAE
   primitive (Section 3.2), binding whole-message application context
   such as a name, version, or policy.  The key schedule commits G by
   appending it as one length-prefixed element after payload_info in the
   commitment info input (Section 4.5), so the commitment covers G just
   as the primitive's ciphertext header T_g does ([FLRR25]).

   G is bound through the commitment, which is fixed when the content is
   created, so G is immutable: changing it invalidates the stored
   commitment.  Segment contents remain rewritable through the snapshot
   authenticator (Section 4.7).

Sullivan                 Expires 14 January 2027               [Page 31]
Internet-Draft                    raAE                         July 2026

   G defaults to the empty octet string, so a message whose application
   supplies no context commits over an empty final element.  G is never
   stored.  A decryptor supplies it from application context, and a
   wrong G fails the commitment check (Section 4.9.1.2) the same way a
   wrong CEK does (Section 6.3).

4.7.  Snapshot Authenticator

   The snapshot authenticator binds the current segment set into the
   public snapshot value.  A SEAL profile selects one with snap_id
   (Table 12).

4.7.1.  Interface and Requirements

   A snapshot authenticator defines a per-segment input, one value per
   segment.  It produces and checks a snapshot value over the (index,
   input) set and the count with two operations:

   snapshot():  Produce the snapshot value over the current (index,
      input) set and the count n_seg.

   verify(snapshot):  Recompute the snapshot value over the current
      (index, input) set and the count and accept only if it matches the
      stored snapshot, in constant time.

   For use in a mutable profile, an authenticator provides one of two
   update styles:

   Incremental:  The authenticator provides add(i, input), remove(i,
      input), and set_length(n).  A single-segment change updates the
      value in place without recomputing it over the whole set.  add
      folds one segment's input into the running value, remove takes one
      back out, and set_length sets the segment count to n.  An
      authenticator that provides add but omits remove rebuilds the
      value by re-adding the surviving segments.

   Recompute-on-change:  The authenticator omits add, remove, and
      set_length.  A single- segment change recomputes the snapshot over
      the updated (index, input) set and the new count and re-publishes
      it.  The consuming protocol serializes concurrent rewrites so that
      each recomputation binds a consistent set.

   SEAL's authenticators define the per-segment input as follows.  The
   masked multiset hash is incremental and uses the segment's AEAD tag
   (Section 4.7.4).  The digest transcript (Section 4.7.5) and the epoch
   digest tree (Section 4.7.6) are recompute-on-change and use the
   segment leaf:

Sullivan                 Expires 14 January 2027               [Page 32]
Internet-Draft                    raAE                         July 2026

   leaf(i) = LH(ct_i) || tag(i)

   This binds the segment's ciphertext through the collision resistance
   of LH and binds the presented nonce, segment index, and finality
   through the tag.

   The scheme's RewriteSeg and SnapVerify (Section 3.3) are realized
   from these operations.  SnapVerify(S, segments, snapshot) checks a
   stored snapshot:

   1.  Let n_seg be the number of present segments.

   2.  Verify that the present segments occupy exactly the indices
       0..n_seg-1, each once.  Finality is not checked here: is_final is
       authenticated by the segment AEAD at decryption, and
       Section 4.9.1.2 rejects a present set whose highest-indexed
       segment does not carry is_final = 1.

   3.  Call verify(snapshot) over the present (index, input) set and the
       count n_seg.

   A duplicate or missing index is caught only by step 2, because an
   authenticator's verify MAY accept a malformed index multiset.  An
   implementation MUST NOT skip the index-set check.  SEAL's masked
   multiset hash realizes verify in Section 4.7.4.

   SnapVerify above checks the whole-object target.  An authenticator
   MAY also support a single-segment target that verifies one (index,
   input) against the recorded set.  The masked multiset hash reads all
   tags and supports the whole-object target only.  The digest
   transcript likewise supports the whole-object target at this layer.
   A consuming protocol that carries its leaf list can verify a single
   segment against an authenticated snapshot value (Section 4.7.5).  The
   snapshot value stays a single target-independent value of length Na,
   with no per-segment proof stored.

   A conforming authenticator's snapshot() MUST bind both the (index,
   input) set and the count n_seg, so a verifier detects any added,
   dropped, reordered, re-marked, or otherwise altered segment, and the
   snapshot value MUST be unforgeable by a party without the
   authenticator's key, under that authenticator's security assumption.
   How the value is computed, and whether a single-segment change
   updates it in place or rebuilds it, is the authenticator's choice.
   The masked multiset hash (Section 4.7.4) updates in place in O(1).
   The digest transcript (Section 4.7.5) recomputes over the full leaf
   list, and the epoch digest tree (Section 4.7.6) recomputes only the
   affected epoch head and the snapshot.

Sullivan                 Expires 14 January 2027               [Page 33]
Internet-Draft                    raAE                         July 2026

   The snapshot value length is written Na and MAY depend on n_seg, with
   Na = 0 meaning no value.  A fixed-length authenticator suits any
   layout, while a count-dependent one is supported only by the split
   layout of a mutable object (Section 4.11).

   A segment's finality is a function of its index and the count:

   is_final(i) = 1 if i = n_seg - 1, else 0

   It is carried by that segment's input.  Because finality follows the
   count, changing n_seg re-marks the boundary segment on its own, so
   rewrite, extend, and truncate are one update over the segments whose
   input changes:

   1.  Under the current count, remove each changed segment's current
       input (remove(i, input)).  The current n_seg fixes which segment
       is terminal, so each removal carries that segment's current
       finality.

   2.  Set the length to the new count (set_length(n)).

   3.  Under the new count, re-encrypt each surviving changed segment
       under its new finality and add its new input (add(i, input)).
       New segments are added here.  Discarded segments are not added
       back.

   4.  Call snapshot(), which recomputes the snapshot value over the new
       count and segment set.

   Each operation is an instance of that shape over different segments:

   +===========+===========+=============+==============+==============+
   | Operation | Count     | Inputs      | Inputs       | Terminal re- |
   |           |           | removed     | added        | mark         |
   +===========+===========+=============+==============+==============+
   | Rewrite   | unchanged | the changed | the changed  | none         |
   |           |           | segment     | segment      |              |
   +-----------+-----------+-------------+--------------+--------------+
   | Extend by | n_seg to  | old         | old          | old final, 1 |
   | k         | n_seg+k   | terminal    | terminal     | to 0 (none   |
   |           |           |             | and k new    | if empty)    |
   +-----------+-----------+-------------+--------------+--------------+
   | Truncate  | n_seg to  | the tail    | new          | new final, 0 |
   | to m      | m         | and old     | terminal     | to 1 (none   |
   |           |           | terminal    |              | if m=0)      |
   +-----------+-----------+-------------+--------------+--------------+

                   Table 6: Rewrite, extend, and truncate

Sullivan                 Expires 14 January 2027               [Page 34]
Internet-Draft                    raAE                         July 2026

   Each operation re-encrypts only the segments whose input changes, so
   a caller that cannot recover a re-encrypted segment's plaintext
   cannot perform it.

   This sequence uses remove, so it is the mutable-authenticator path.
   An authenticator that omits remove instead resets and re-adds the
   surviving segments to rebuild the snapshot value.

4.7.2.  Selecting an Authenticator

   A SEAL profile selects its snapshot authenticator with snap_id
   (Table 12): 0x0000 for none, 0x0001 for the masked multiset hash,
   0x0002 for the digest transcript, or 0x0003 for the epoch digest
   tree.

4.7.3.  None

   With snap_id = 0x0000 there is no snapshot authenticator: no snapshot
   value is produced (Na = 0), snapshot() and verify() are no-ops, and a
   mutable object rests on per-segment authentication alone.  Such a
   profile MUST NOT produce the empty object (n_seg = 0).  With no
   snapshot value to bind the zero count, a truncation that removed
   every segment would be indistinguishable from a legitimate empty
   object, so a valid object always has at least one segment
   (Section 6.6).

4.7.4.  Masked Multiset Hash

   With snap_id = 0x0001 the snapshot authenticator builds an updatable,
   masked multiset hash over the segment tags.  It accumulates one
   indexed, keyed contribution per segment, in the MSet-XOR-Hash form of
   Clarke et al.  ([MSetHash]), then publishes that accumulator behind a
   deterministic mask this document adds.  Its per-segment contribution
   is:

   contrib(i) = KDF(protocol_id, contrib_label,
                    [snap_key],
                    [uint64(i), tag(i)], Nh)

   The masked multiset hash fixes three labels: contrib_label =
   "acc_contrib", snapshot_tag_label = "snapshot_tag", and
   snapshot_mask_label = "snapshot_mask", each distinct from all other
   SEAL labels.

Sullivan                 Expires 14 January 2027               [Page 35]
Internet-Draft                    raAE                         July 2026

   The finality bit is bound through tag(i): it is an AEAD input in both
   nonce modes (segment_aad in random mode, the low nonce bit in derived
   mode, Section 4.5.3.2), so flipping it changes tag(i), hence
   contrib(i), hence the accumulator.  No explicit binding in contrib is
   needed.

   The masked multiset hash aggregates the per-segment contributions
   into the accumulator acc by bitwise XOR:

   acc = contrib(0) XOR contrib(1) XOR ... XOR contrib(n_seg-1)

   The accumulator is an Nh-octet value, with the all-zero string as the
   value for the empty segment set.  XOR is order-independent, so the
   accumulator does not depend on segment order, and it is its own
   inverse, so rewriting segment i removes its old contribution and adds
   the new one by XOR, in O(1) time and without re-reading any other
   segment.

   An object with n_seg = 0 is a valid empty object.  It holds the salt,
   the commitment, and the snapshot value, with no ciphertext segments
   and no per-segment tags.  Its accumulator is the XOR over the empty
   segment set, namely 0^Nh, and the same masking as any other count
   applies, so the empty snapshot is not a fixed constant but a masked
   authenticator over zero segments:

   acc          = 0^Nh
   snapshot_tag = snaptag(0, 0^Nh)
   mask         = snapmask(0, snapshot_tag)
   snapshot     = snapval(0, 0^Nh) = mask || snapshot_tag

   An empty object is distinct from a zero-length plaintext, which is
   one final segment (n_seg = 1) whose plaintext has length 0.

   To rewrite segment i without re-reading the other tags, the writer
   first recovers the accumulator from the stored snapshot by removing
   the mask, acc = wrapped_acc XOR snapmask(n_seg, snapshot_tag).  It
   then XORs contrib(i) computed from the old tag (stored alongside the
   ciphertext) out of acc, XORs in contrib(i) computed from the new tag,
   and produces the new snapshot value over the unchanged count.  The
   wrapped_acc and snapshot_tag MUST come from locally trusted snapshot
   state (Section 4.9.2.1).

   The accumulator is linear, and publishing it in the clear would let a
   write adversary recombine the differences between successive values
   into a non-historical segment set (Appendix C.5).  The masked
   multiset hash therefore does two things.  First it authenticates the
   count and accumulator with the snapshot tag, a message authentication
   code (MAC) under snap_key with its own label:

Sullivan                 Expires 14 January 2027               [Page 36]
Internet-Draft                    raAE                         July 2026

   snaptag(n_seg, acc) = KDF(protocol_id, snapshot_tag_label,
                             [snap_key],
                             [uint64(n_seg), acc], Nh)

   Write snapshot_tag = snaptag(n_seg, acc) for the present count and
   accumulator.  Second, the masked multiset hash derives a mask from
   snap_key under the third label, seeded by that synthetic tag rather
   than by a fresh nonce, and XORs it into the accumulator to form the
   masked accumulator wrapped_acc:

   snapmask(n_seg, snapshot_tag) =
       KDF(protocol_id, snapshot_mask_label,
           [snap_key],
           [uint64(n_seg), snapshot_tag], Nh)

   mask        = snapmask(n_seg, snapshot_tag)
   wrapped_acc = acc XOR mask
   snapshot    = wrapped_acc || snapshot_tag

   Seeding the mask with the synthetic tag rather than a fresh nonce is
   the synthetic-IV derandomization of deterministic authenticated
   encryption ([DAE]): the snapshot stays a deterministic function of
   (n_seg, acc) while the published accumulator is hidden behind a one-
   time pad (Appendix C.5).  The count n_seg is not stored in the
   snapshot value.  The verifier supplies it as the number of segments
   present, and snaptag binds it under the MAC.

   The masked multiset hash stores wrapped_acc and snapshot_tag, each Nh
   octets, so Na = 2*Nh.  Section 4.7.1 defines Na in general.

   As a function of the count and accumulator, the snapshot value is

   snapval(n_seg, acc) = wrapped_acc || snapshot_tag

   The snapshot tag binds n_seg and the accumulator under snap_key, and
   the mask hides the accumulator behind a tag-derived one-time pad.
   The forgery bound and the masking argument are in Section 5.8 and
   Appendix C.5.

   SnapVerify runs only after the verifier has re-derived and checked
   the commitment as part of decryption (Section 4.9.1.2).  The
   snapshot's per-object binding rests on snap_key (Appendix C.5).

   The masked multiset hash realizes verify(snapshot) for the SnapVerify
   of Section 4.7.1.  SnapVerify supplies the count n_seg and the
   present segment tags:

Sullivan                 Expires 14 January 2027               [Page 37]
Internet-Draft                    raAE                         July 2026

  verify(snapshot):
    ;; Caller (SnapVerify, {{snapshot-interface}}) MUST already have
    ;; checked that the present indices are exactly 0..n_seg-1, each
    ;; once.  This accumulator is order-independent under XOR, so a
    ;; duplicate or missing index is invisible here and MUST NOT be
    ;; skipped by the caller.
    acc_calc      = XOR over i of contrib(i)   ;; present segment tags
    tag_calc      = snaptag(n_seg, acc_calc)
    snapshot_calc = (acc_calc XOR snapmask(n_seg, tag_calc)) || tag_calc
    compare snapshot_calc to snapshot in constant time
            (on any mismatch, reject)
    return accept

   The comparison runs in constant time (Section 6.9).  Because the
   accumulator is order-independent under XOR, a duplicate or missing
   index can leave acc_calc recomputing to the genuine accumulator and
   the comparison passing, which is why SnapVerify's index-set check
   (Section 4.7.1) is mandatory.

   verify returns only accept or reject.  An implementation MUST NOT
   surface the recomputed accumulator or any value derived from it, and
   MUST NOT signal through an error code or timing whether the snapshot
   tag matched while the masked accumulator did not.  The mask hides the
   accumulator only against a one-bit verifier: exposing it, or
   distinguishing a tag-valid accumulator-mismatch from a plain reject,
   reinstates the recombination attack of Appendix C.5.

   A modified tag or count changes the recomputed snapshot, and a
   different key or parameter context fails the commitment check that
   precedes SnapVerify (Section 4.9.1.2).  The forgery bound is in
   Section 5.8 and the rollback limitation in Section 6.6.

4.7.5.  Digest Transcript

   With snap_id = 0x0002 the snapshot authenticator is a per-object
   digest transcript: one keyed KDF evaluation over the ordered list of
   per-segment digests.  It has no accumulator and no mask, and it
   recomputes over the full leaf list on any rewrite.  Its
   distinguishing property is that the snapshot value binds each
   segment's ciphertext bytes, not only its AEAD tag, so the binding
   holds even against an adversary that holds the CEK and every key
   derived from it (Section 5.8.2).  A consuming protocol that
   authenticates the snapshot value, for example by binding it into a
   signed reference or manifest, extends that binding to per-segment
   origin authentication.

   Its per-segment input is the segment leaf, the ciphertext digest
   concatenated with the AEAD tag:

Sullivan                 Expires 14 January 2027               [Page 38]
Internet-Draft                    raAE                         July 2026

   leaf(i) = LH(ct_i) || tag(i)

   LH is the length-prefix digest of Section 4.3, so LH(ct_i) is a fixed
   Nh-octet value that binds segment i's ciphertext through the
   collision resistance of LH for any segment length, and leaf(i) is the
   fixed Nh + Nt octet concatenation of that digest with the segment's
   AEAD tag.  The ciphertext digest is a deterministic function of
   public object bytes, so any party can recompute or publish it without
   holding a key.  The tag is the value the AEAD already produced for
   the segment.  leaf(i) is the digest transcript's per-segment input,
   taking the role Section 4.7.1 assigns the tag for an Incremental
   authenticator.

   The leaf binds the segment's index and finality, and the presented
   nonce, through the tag.  The tag authenticates the segment AAD, which
   carries the index and is_final in random nonce mode and is empty in
   derived nonce mode where the nonce carries them (Section 4.4.2), and
   the tag is a function of the presented nonce, so a segment cannot be
   reinterpreted under a different nonce without changing the tag and
   hence the leaf.  Position within the object is bound by the ordered
   transcript below and the count by n_seg.  Because the tag binds the
   index and finality in either nonce mode, snap_id 0x0002 places no
   restriction on the nonce mode.

   The snapshot value is one keyed derivation over the commitment, the
   count, and the ordered leaf list:

   snapshot = KDF(protocol_id, transcript_label, [snap_key],
                  [commitment, uint64(n_seg),
                   leaf(0), ..., leaf(n_seg-1)], Nh)

   so Na = Nh.  The digest transcript fixes one label, transcript_label
   = "snap_transcript", distinct from all other SEAL labels under the
   encode frame (Section 4.3).  The injective framing of the ordered
   list binds each leaf's position and the count, so a leaf cannot move
   to another index without changing the transcript.  The snap_key ikm
   makes the value unforgeable without the key (Section 4.7.1).  The
   commitment element repeats the object's commitment (Section 4.6)
   inside the transcript.  For SnapVerify alone it is redundant, because
   snap_key already binds the CEK and salt.  It is load-bearing for a
   consuming protocol that lifts the snapshot value into a signature or
   MAC, which then carries the full object context (CEK, payload_info,
   and G) and cannot be replayed against another object (Section 5.8.2).

   The digest transcript realizes verify(snapshot) as follows.  The
   caller (SnapVerify, Section 4.7.1) MUST already have checked that the
   present indices are exactly 0..n_seg-1, each once.

Sullivan                 Expires 14 January 2027               [Page 39]
Internet-Draft                    raAE                         July 2026

   verify(snapshot):
     ;; The caller has checked the present index set (see above).
     for i in 0 .. n_seg-1:
         leaf_calc(i) = LH(ct_i) || tag(i) over the stored segment i
     snapshot_calc = KDF(protocol_id, transcript_label, [snap_key],
                         [commitment, uint64(n_seg),
                          leaf_calc(0), ..., leaf_calc(n_seg-1)], Nh)
     compare snapshot_calc to snapshot in constant time
             (on any mismatch, reject)
     return accept

   The comparison runs in constant time (Section 6.9).  The index-set
   check in SnapVerify remains mandatory (Section 4.7.1).  Here it is
   defense in depth rather than load-bearing, because a malformed index
   multiset changes the framed leaf list and the recomputed value with
   it.

   The digest transcript is per-object (Section 4.7.1): it provides no
   add, remove, or set_length, and produces the snapshot in a single KDF
   call over the leaf list.  Any change to the segment set recomputes
   the transcript over the new leaf list.  Under a write-once profile
   the value is computed once at encryption and only verified
   thereafter.  Under a mutable profile a rewrite recomputes the
   transcript over the updated leaf list and re-publishes the new
   snapshot value (Section 4.10.2).

   An object with n_seg = 0 is a valid empty object under the digest
   transcript.  The leaf list is empty and the snapshot value is
   KDF(protocol_id, transcript_label, [snap_key], [commitment,
   uint64(0)], Nh), so the count is bound and truncation to the empty
   object is distinguishable from a legitimate empty object.

   verify above recomputes each leaf from the stored segment bytes, so
   whole-object SnapVerify reads the full object.  A layout or consuming
   protocol MAY store or carry the leaf list itself: the aligned and
   split layouts hold leaf(i) in each per-segment metadata entry
   (Section 4.11.2), and a consuming protocol using the linear layout
   MAY carry the list beside the object, for example in a manifest.  A
   reader MAY then verify the transcript over the stored or carried
   leaves without reading any ciphertext, and MUST compare leaf(i),
   recomputed from the one segment it reads, against the stored or
   carried entry at position i before treating that segment as covered
   by the snapshot.  A segment whose recomputed leaf does not match
   fails authentication.  This is the single-segment pattern of
   Section 4.7.1, and it is how a consuming protocol obtains per-segment
   origin authentication from one authenticated snapshot value.

Sullivan                 Expires 14 January 2027               [Page 40]
Internet-Draft                    raAE                         July 2026

   A modified segment, tag, or count changes the recomputed transcript,
   and a different key or parameter context fails the commitment check
   that precedes SnapVerify (Section 4.9.1.2).  The forgery and binding
   arguments are in Section 5.8.2.

4.7.6.  Epoch Digest Tree

   With snap_id = 0x0003 the snapshot authenticator is an epoch digest
   tree.  Segment leaves (Section 4.7.5) fold into per-epoch heads, and
   the heads fold into the snapshot.  A reader verifies one segment from
   two aligned metadata reads plus the segment itself: the target
   epoch's leaf run, and the epoch-heads region.  Each of those two
   metadata reads is one segment_max or less for objects up to roughly
   128 GB at the recommended parameters (Table 7).  The design has the
   digest transcript's binding properties and adds this bounded random-
   access verification for large objects.

    epoch 0                                     epoch E-1
 +---------------------------+             +---------------------------+
 | ct(0) ct(1) ... ct(2^r-1) |     ...     | ct(...) ...  ct(n_seg-1)  |
 +---------------------------+             +---------------------------+
                 |                                       |
        keyed transcript over                   keyed transcript over
        the epoch's leaves                      the epoch's leaves
                 |                                       |
               d(0)                                    d(E-1)
                 +-------------------+-----+-----+--------+
                                     |
                                 keyed transcript
                                 over epoch heads
                                     |
                                  snapshot

   Figure 4: Epoch digest tree: leaves in each epoch are folded by a
     keyed transcript into one epoch head, and the epoch heads are
      folded by a keyed transcript into the snapshot.  There is no
                              Merkle path

   The leaf is:

   L(i) = leaf(i) = LH(ct_i) || tag(i)

   d_e is the epoch head over one epoch's leaves, and the snapshot is
   the keyed digest over d_0 ..  d_{E-1}.

   Its per-segment leaf is leaf(i) = LH(ct_i) || tag(i), exactly as in
   Section 4.7.5.

Sullivan                 Expires 14 January 2027               [Page 41]
Internet-Draft                    raAE                         July 2026

   Segments are grouped into epochs of 2^r consecutive indices, where r
   = epoch_length (Section 4.5.2) is reused as the grouping fan-out, so
   no new parameter is added.  Each epoch holds 2^r consecutive
   segments.  Only the final epoch may be short.

   epoch e  =  segments  e * 2^r  ..  min((e+1) * 2^r, n_seg) - 1
   E = ceil(n_seg / 2^r) epochs

   There are E epochs, the same grouping the epoch key uses, so each
   epoch head covers exactly the segments under one epoch key.

   The epoch head d_e is a keyed digest over that epoch's leaves:

   epoch_run(e) = leaf(e * 2^r) || ... || leaf(last index in epoch e)
   d_e = KDF(protocol_id, epoch_head_label, [snap_key],
             [LH(epoch_run(e))], Nh)

   The leaves are fixed length (Nh + Nt octets) and the epoch heads are
   fixed length (Nh octets), so each run parses unambiguously given its
   element count, which n_seg and epoch_length fix.

   An epoch head needs no index or count of its own.  Its position is
   bound by where d_e sits in the ordered heads_run, exactly as a leaf's
   position is bound by the ordered transcript in Section 4.7.5.  The
   final epoch's size is fixed by n_seg and epoch_length, both already
   bound (n_seg in the snapshot below, epoch_length in snap_key through
   payload_info), so a partial final epoch needs no explicit count.

   Because the epoch heads carry no index or count of their own and the
   segmentation is recovered only from n_seg and epoch_length, a profile
   that selects the epoch digest tree MUST bind epoch_length into
   snap_key.  SEAL satisfies this through payload_info, which carries
   epoch_length into the snap_key derivation.  A profile that left
   epoch_length unbound would let a key holder present one heads_run
   under a different fan-out, defeating the position and count binding
   above.

   A full leaf run can be larger than the 0xFFFE-octet frame literal
   (Section 4.3), so LH compresses it to a fixed Nh octets before it
   enters the KDF, and every element the KDF sees stays at most Nh
   octets.

   The snapshot value is a keyed digest over the epoch heads:

   heads_run = d_0 || d_1 || ... || d_{E-1}
   snapshot = KDF(protocol_id, head_label, [snap_key],
                  [commitment, uint64(n_seg), LH(heads_run)], Nh)

Sullivan                 Expires 14 January 2027               [Page 42]
Internet-Draft                    raAE                         July 2026

   so Na = Nh.  LH pre-hashes the heads run for the same reason.  The
   epoch digest tree fixes two labels, epoch_head_label = "snap_epoch"
   and head_label = "snap_epoch_root", each distinct from all other SEAL
   labels under the encode frame (Section 4.3).  The commitment and
   count are bound as in Section 4.7.5, and the snap_key ikm makes the
   value unforgeable without the key (Section 4.7.1).

   Because the leaf binds the tag (Section 4.7.5), the epoch digest tree
   places no restriction on the nonce mode.

   A reader stores the E epoch heads in the header alongside the
   snapshot value, and the per-segment leaves in the metadata entries.
   Section 4.11.2 gives the layout, and the snapshot value is Nh octets.
   The epoch heads are recomputable from the leaves, so an
   implementation MAY store none of them and recompute them, at the cost
   of reading every leaf.

   The two-read random-access verification below requires a layout whose
   per-segment leaves are contiguous within an epoch, so one seek reads
   the whole epoch: the aligned layout (Section 4.11.2) or the split
   layout (Section 4.11.3).  Under the linear layout the leaves are
   interleaved with the ciphertext, so this property does not hold and a
   reader gathers an epoch's leaves with one seek per segment.

   verify(snapshot) recomputes the whole transcript.  The caller
   (SnapVerify, Section 4.7.1) MUST already have checked that the
   present indices are exactly 0..n_seg-1, each once.

   verify(snapshot):
     ;; The caller has checked the present index set (see above).
     for e in 0 .. E-1:
         d_calc(e) = KDF(protocol_id, epoch_head_label, [snap_key],
                         [LH(epoch_run(e))], Nh)
     snapshot_calc = KDF(protocol_id, head_label, [snap_key],
                         [commitment, uint64(n_seg),
                          LH(d_calc(0) || ... || d_calc(E-1))], Nh)
     compare snapshot_calc to snapshot in constant time
             (on any mismatch, reject)
     return accept

   Random-access verification of one segment costs two aligned reads
   beyond the segment itself.  A reader verifies the head once and keeps
   the epoch heads, then per segment:

Sullivan                 Expires 14 January 2027               [Page 43]
Internet-Draft                    raAE                         July 2026

   verify_segment(i):    ;; aligned or split layout
     ;; once per object, then cached:
     read the epoch-heads region (E * Nh octets), which follows the
         snapshot value in the header ({{aligned-layout}})
     head_calc = KDF(protocol_id, head_label, [snap_key],
                     [commitment, uint64(n_seg), LH(d_0 || ... ||
                      d_{E-1})], Nh)
     check head_calc equals the stored snapshot (constant time)
     ;; per segment i:
     e = i >> epoch_length
     read epoch e's leaf run, min(2^r, n_seg - e * 2^r) leaves,
         from segment e * 2^r's metadata entry ({{aligned-layout}})
         ;; one aligned read
     d_calc = KDF(protocol_id, epoch_head_label, [snap_key],
                  [LH(epoch_run(e))], Nh)
     check d_calc equals the cached d_e (constant time)
     read segment i from the data region ({{aligned-layout}})
     leaf_calc = LH(ct_i) || tag(i)
     check leaf_calc equals the stored leaf at position i mod 2^r
         within epoch e's run (constant time)

   A cold single-segment check is two metadata reads, the epoch-heads
   region and one epoch's leaf run, each one aligned read up to the
   ceiling (Table 7).  A reader scanning many segments reads the epoch-
   heads region once and then one epoch leaf run per epoch, so the
   amortized cost is one metadata read per segment.

   All comparisons run in constant time (Section 6.9).  A segment whose
   recomputed leaf, epoch head, or head does not match fails
   authentication.  The reader recomputes only its own leaf and its own
   epoch head and reads no other segment's ciphertext.

   The epoch digest tree is per-object (Section 4.7.1): it provides no
   add, remove, or set_length.

   A rewrite of segment i recomputes only epoch e's head and the
   snapshot value, where e = i >> epoch_length, leaving the other epoch
   heads unchanged.  As with any keyed snapshot, a consuming protocol
   that signed the value re-signs the new one.  Under a write-once
   profile the value is computed once at encryption.  Under a mutable
   profile a rewrite is the same two aligned metadata reads as
   verification (the epoch's leaf run and the epoch-heads region)
   followed by three metadata writes: the new leaf, the new epoch head,
   and the new snapshot value (Section 4.10.2).

   An object with n_seg = 0 has E = 0 and an empty heads run:

Sullivan                 Expires 14 January 2027               [Page 44]
Internet-Draft                    raAE                         July 2026

   snapshot = KDF(protocol_id, head_label, [snap_key],
                  [commitment, uint64(0), LH("")], Nh)

   so the count is bound and truncation to the empty object is
   distinguishable.

   Choose the largest epoch_length whose epoch leaf run still fits one
   aligned read (Section 4.11.2), so recomputing an epoch head is a
   single read:

   2^r * meta_len  <=  segment_max

   The values below are for a derived-nonce entry (Np = 0, meta_len = Nt
   + Nh).  A random-nonce entry has meta_len = Np + Nt + Nh.  Recompute
   r from the rule above.

            +=============+==========+========================+
            | segment_max | Nh       | Suggested epoch_length |
            +=============+==========+========================+
            | 65536       | 32 or 48 | 10                     |
            +-------------+----------+------------------------+
            | 65536       | 64       | 9                      |
            +-------------+----------+------------------------+
            | 16384       | 32 or 48 | 8                      |
            +-------------+----------+------------------------+
            | 16384       | 64       | 7                      |
            +-------------+----------+------------------------+

               Table 7: Suggested epoch_length by segment_max
                          and KDF output length Nh

   The epoch-heads region of the header is E * Nh octets, one aligned
   read up to about 128 GiB at r = 10, Nh = 32, and segment_max 65536.
   Past that it spans 1 + ceil(content size / that ceiling) reads.  A
   tree over the epoch heads would restore a single bounded read but is
   out of scope.

   The forgery and binding arguments are in Section 5.8.2.

4.8.  Segment Algorithms

   The construction defines the per-segment subroutines EncryptSegment,
   DecryptSegment, and RewriteSegment.  In this random-access
   construction, is_final is an explicit input to each subroutine.
   Full-message encryption derives it from the total segment count
   n_seg, while random-access decryption and rewrite callers supply it
   directly.  The full-message procedures (Section 4.9.1.1,
   Section 4.9.1.2, Section 4.9.2.2) and the rewrite procedure

Sullivan                 Expires 14 January 2027               [Page 45]
Internet-Draft                    raAE                         July 2026

   (Section 4.9.2.1) compose them.

   Each subroutine is the cryptographic core of one operation of the
   base or extended raAE interface (Section 3.2, Section 3.3), refining
   the operation-level mapping of Table 1 to the signature level.  The
   primitive's position identifier p = (i, b) appears here as the
   explicit arguments i and is_final.  Its immutable state S is realized
   by the payload schedule: the segment_key(i), nonce(i), and
   segment_aad calls below read the schedule from context rather than
   taking S as an argument.  Its message segment M_i is the plaintext
   P_i, and its opaque ciphertext segment C_i is stored as the field
   triple (nonce_metadata(i), ct_i, tag_i).

   +================+====================+============================+
   | Subroutine     | Realizes           | Composed by                |
   +================+====================+============================+
   | EncryptSegment | EncSeg             | Section 4.9.1.1,           |
   |                |                    | Section 4.9.2.2            |
   +----------------+--------------------+----------------------------+
   | DecryptSegment | DecSeg             | Section 4.9.1.2            |
   +----------------+--------------------+----------------------------+
   | RewriteSegment | the encryption     | Section 4.9.2.1 and        |
   |                | core of RewriteSeg | Section 4.9.2.2, which add |
   |                |                    | the snapshot bookkeeping   |
   +----------------+--------------------+----------------------------+

      Table 8: Segment subroutines and the primitive operations they
                                 realize

   These subroutines emit ct_i and tag_i as separate fields, and ct_i
   has length len(P_i), which can be shorter than segment_max.  The
   consuming format conveys each segment's ciphertext length to the
   decoder so DecryptSegment can recover the two fields, as required by
   Section 4.4.2.

   In every subroutine, i is the segment index (a non-negative integer),
   is_final is 1 for the last segment and 0 for all others, A_i is the
   caller-supplied associated data for segment i, and P_i is the segment
   plaintext.  The subroutines call segment_key(i) and nonce(i) as
   defined in Section 4.5, with nonce(i) following the chosen
   nonce_mode, and segment_aad(i, is_final, A_i) as defined in
   Section 4.4.2.

   nonce_metadata(i) denotes the stored nonce metadata for segment i:
   nonce(i) when nonce_mode is "random", and empty when it is "derived".
   In random mode DecryptSegment reads its nonce from nonce_metadata_i,
   and in derived mode it recomputes nonce(i).

Sullivan                 Expires 14 January 2027               [Page 46]
Internet-Draft                    raAE                         July 2026

4.8.1.  EncryptSegment

   EncryptSegment(i, is_final, A_i, P_i):
     key   = segment_key(i)
     nonce = nonce(i)                        ;; per nonce_mode
     aad   = segment_aad(i, is_final, A_i)
     C_i   = AEAD.Encrypt(key, nonce, aad, P_i)
     split C_i into ct_i and tag_i
     return (nonce_metadata(i), ct_i, tag_i)

   AEAD.Encrypt and AEAD.Decrypt are the interfaces of [RFC5116]
   Sections 2.1 and 2.2.  For the AEADs in Table 10 the tag is the final
   Nt octets of C_i, so ct_i is the first len(P_i) octets and C_i =
   ct_i || tag_i.  EncryptSegment splits C_i at that boundary, and
   DecryptSegment reassembles it.

   The caller supplies the segment's input to the snapshot authenticator
   (Section 4.7).  The input is the tag tag_i for the masked multiset
   hash and the leaf LH(ct_i) || tag(i) for the digest transcript.
   EncryptSegment itself does no snapshot work.

4.8.2.  DecryptSegment

   DecryptSegment returns the segment plaintext, or a decryption error:

   DecryptSegment(i, is_final, A_i, nonce_metadata_i, ct_i, tag_i):
     key   = segment_key(i)
     nonce = nonce_metadata_i or nonce(i)    ;; per nonce_mode (above)
     aad   = segment_aad(i, is_final, A_i)
     C_i   = ct_i || tag_i
     P_i   = AEAD.Decrypt(key, nonce, aad, C_i)
             (on verification failure, return a decryption error)
     return P_i

4.8.3.  RewriteSegment

   RewriteSegment assumes the caller has already verified the commitment
   or otherwise confirmed it holds the correct CEK.  A rewrite under the
   wrong key produces unreadable ciphertext and corrupts the snapshot
   authenticator's state.  The error is detectable only on a subsequent
   read.

   RewriteSegment(i, is_final, A_i, new_P_i) is defined as
   EncryptSegment(i, is_final, A_i, new_P_i): a rewrite is a fresh
   encryption of the new plaintext new_P_i.  The output
   (new_nonce_metadata_i, new_ct_i, new_tag_i) replaces the stored nonce
   metadata, ciphertext, and tag for segment i.

Sullivan                 Expires 14 January 2027               [Page 47]
Internet-Draft                    raAE                         July 2026

   RewriteSegment produces the replacement ciphertext and tag only.  The
   caller updates the snapshot authenticator by reading the stored old
   tag and calling remove(i, old_tag_i) then add(i, new_tag_i), or
   rebuilds if the authenticator omits remove (Section 4.9.2.1).

4.9.  Top-level Algorithms

   The top-level algorithms compose the segment subroutines
   (Section 4.8) over complete messages.  They call the snapshot-
   authenticator operations of Section 4.7 (snapshot() and verify() in
   every profile, and add(i, input), remove(i, input), and set_length(n)
   in a mutable profile) without referring to how any is computed.  The
   authenticator selected by snap_id (Table 12) realizes those
   operations.  For the masked multiset hash they reduce to the
   accumulator, snapshot-tag, and mask formulas of Section 4.7.4.  For
   the digest transcript and epoch digest tree the mutable-interface
   operations reduce to a recomputation of the snapshot over the updated
   (index, input) set.

   These procedures do not expose per-segment associated data, so they
   pass an empty A_i to each subroutine.  An application that needs per-
   segment A_i calls the subroutines directly.

4.9.1.  Read-Only Operations

   Encryption and decryption are available in every SEAL profile.  They
   write a message once and read individual segments.  Neither rewrites
   a stored segment.  When the profile configures no snapshot
   authenticator (snap_id = 0x0000, Table 12), the snapshot steps below
   are omitted, and a reader relies on per-segment authentication alone.

4.9.1.1.  Encryption

   Encryption runs in two phases.  The first phase derives the per-
   message schedule and writes the commitment.  The second encrypts each
   segment independently and feeds its input to the snapshot
   authenticator, and then snapshot() binds the whole (index, input) set
   and the count into the stored snapshot value.  Because the segments
   are independent, the second phase may run in any order or in
   parallel.

Sullivan                 Expires 14 January 2027               [Page 48]
Internet-Draft                    raAE                         July 2026

      CEK, salt
          |
          v
     payload schedule
          |
     .----+----------+----------.
     v               v          v
   commitment   payload_key   snap_key
                     |
                     v
      EncryptSegment(i, P_i)   using payload_key   (one per segment)
                     |
                     v
                ct_i, tag_i

               Figure 5: Key Schedule and Segment Encryption

   The per-segment inputs then feed the snapshot authenticator, which
   binds them into the snapshot value (Section 4.7).  For the masked
   multiset hash the input is the AEAD tag.  For the digest transcript
   and the epoch digest tree it is the segment leaf leaf(i) =
   LH(ct_i) || tag(i) (Section 4.7.5, Section 4.7.6).  An Incremental
   authenticator MAY maintain the value with add and set_length as each
   segment is produced.  A Recompute-on-change authenticator computes
   snapshot() over the full set.

   An application that writes a new message MUST generate a fresh,
   uniformly random salt.  Given a CEK, that salt, a parameter set, G,
   and a plaintext P split into segments P_0 through P_{n_seg-1}:

   derive  commitment, payload_key, snap_key from the payload schedule
           (and nonce_base, in derived mode)
   store   commitment

   for i in 0 .. n_seg-1:
       is_final = 1 if i = n_seg-1, else 0
       nonce_metadata_i, ct_i, tag_i
           = EncryptSegment(i, is_final, empty, P_i)
       store nonce_metadata_i, ct_i, tag_i

   if snapshot authentication is enabled (snap_id != 0x0000):
       snapshot = snapshot()   ;; over the (index, input) set and n_seg
       store snapshot

   A zero-length plaintext produces a single segment of length 0 with
   is_final = 1.

Sullivan                 Expires 14 January 2027               [Page 49]
Internet-Draft                    raAE                         July 2026

4.9.1.2.  Decryption of Segment i

   Opening a segment is a local operation: check the commitment to
   reject a wrong key early, then AEAD-decrypt the one segment.  This
   yields per-segment authenticity over the ciphertext core, the segment
   index, and the finality bit.  Under a snapshot authenticator (snap_id
   != 0x0000), whole-set authenticity is a separate and stronger check
   that requires the full present segment set.  It detects any added,
   dropped, reordered, or re-marked segment, and same-index rollback to
   an older sibling.  A reader holding only one segment gets per-segment
   authenticity but MUST NOT claim snapshot integrity until it runs
   SnapVerify (Section 4.7).  A profile with no snapshot authenticator
   (snap_id = 0x0000) defines no snapshot, so per-segment authenticity
   is the whole integrity guarantee.

   Given the CEK, salt, parameter set, G, segment index i, finality
   status, and the stored ciphertext segment for index i:

 derive  commitment from CEK, salt, and G
 derive  payload_key, snap_key from CEK and salt
 if stored commitment != derived commitment:  return error
 P_i = DecryptSegment(i, is_final, empty, nonce_metadata_i, ct_i, tag_i)
        (return its error on failure)
 if snapshot authentication is enabled and the full set is available:
     if SnapVerify(snapshot) fails:  return error  (do not deliver P_i)
 return P_i

   Here SnapVerify is the three-argument SnapVerify(S, segments,
   snapshot) of Section 3.3, with S the derived schedule and segments
   the present set: (index, tag) pairs for the masked multiset hash, and
   the stored segments themselves for the digest transcript
   (Section 4.7.5).

   SnapVerify needs its per-segment input for every segment (each tag at
   snap_id 0x0001, each leaf at 0x0002 and 0x0003), so a reader that
   streams segments in order cannot gate their release on snapshot
   integrity without first buffering those inputs or using a layout that
   carries them in a header.  Delivering on per-segment authenticity
   alone forgoes snapshot freshness for that segment (Section 6.6).  A
   streaming reader likewise cannot rule out truncation before the end
   of the stream: plaintext delivered before the highest-indexed segment
   verifies under is_final = 1, and before SnapVerify passes where a
   snapshot authenticator is configured, MUST be treated as unverified
   with respect to completeness.

Sullivan                 Expires 14 January 2027               [Page 50]
Internet-Draft                    raAE                         July 2026

   A decryptor MUST reject a truncated object, one whose highest-indexed
   segment does not carry is_final = 1.  A run of segments 0 through k-1
   whose last segment (index k-1) carries is_final = 0 is incomplete,
   and the decryptor MUST fail rather than return the partial plaintext.

   The check works because the application supplies the segment count
   n_seg, from which the decryptor derives the finality each segment
   must carry (Section 4.8).  It then verifies the final segment under
   is_final = 1.  A truncated object, whose last surviving segment was
   written as non-final, fails that verification.  The finality bit is
   bound per segment and is unforgeable without the CEK: in derived
   nonce mode it occupies the low bit of the segment nonce
   (Section 4.5.3), and in random nonce mode it is bound through
   segment_aad (Section 4.4.2).  An adversary therefore cannot re-mark a
   non-final segment as final and can only drop trailing segments, which
   this check detects.

   Authorized truncation by the CEK holder re-encrypts the new final
   segment under is_final = 1 with a fresh nonce (Section 4.9.2.2), so a
   legitimately truncated object still ends with is_final = 1 and
   verifies.  This check needs no snapshot authenticator.

   Truncation to an empty object is covered too.  A profile with no
   snapshot authenticator forbids the zero-segment object, so a valid
   object always has at least one segment, and a decryptor MUST reject
   one that has none.  Under a snapshot authenticator the snapshot binds
   the count, so the empty object (n_seg = 0) is a distinct
   authenticated state (Section 6.6).

   On a commitment mismatch the implementation MUST NOT proceed with
   decryption, SHOULD zeroize derived key material, and MUST return an
   error.  Under a snapshot authenticator, when the full present set is
   available (for example, a full read or an integrity audit), the
   reader MUST run SnapVerify (Section 4.7) on the stored snapshot
   before delivering any plaintext, and MUST return an error if
   SnapVerify fails.  With no snapshot authenticator there is no such
   check, and per-segment authentication stands alone.

   Decryption can fail in up to three distinguishable ways:

Sullivan                 Expires 14 January 2027               [Page 51]
Internet-Draft                    raAE                         July 2026

       +===========================+===============================+
       | Failure                   | Cause                         |
       +===========================+===============================+
       | Commitment mismatch       | wrong key or parameters       |
       +---------------------------+-------------------------------+
       | AEAD verification failure | corrupted or tampered segment |
       +---------------------------+-------------------------------+
       | Snapshot mismatch         | segment set or count modified |
       |                           | (snapshot authenticator only) |
       +---------------------------+-------------------------------+

                   Table 9: Decryption failure conditions

   Implementations SHOULD report these as distinct error conditions for
   local diagnosis, but SHOULD return a single opaque error to untrusted
   callers to avoid leaking an oracle (for example, distinguishing
   "wrong key" from "tampered segment" over a network).

4.9.2.  Rewritable Operations

   Rewriting and length change are available only in a mutable profile
   (Section 4.10.2), which admits snap_id 0x0001, 0x0002, or 0x0003.
   The procedures below are written against the Incremental interface
   (Section 4.7.1) used by the masked multiset hash.  Under a Recompute-
   on-change authenticator (the digest transcript or the epoch digest
   tree) the remove, add, and set_length calls in these procedures
   collapse into a single snapshot() over the updated (index, leaf) set,
   and the operations are otherwise identical.  A caller that cannot
   recover a re-encrypted segment's plaintext cannot perform them.

4.9.2.1.  Rewriting Segment i

   A rewrite touches exactly one segment's ciphertext.  Replacing the
   segment removes its old tag from the snapshot authenticator and adds
   the new one, then updates the snapshot over the unchanged count with
   snapshot().  Under the Incremental interface the update trusts,
   rather than re-verifies, the stored snapshot state.  The old tag is
   already stored alongside the old ciphertext, so no other segment is
   read or decrypted (Section 4.7).  Under a Recompute-on-change
   authenticator the snapshot() call recomputes the value from the
   stored per-segment leaves, and no stored snapshot state is trusted.

   The procedure is:

Sullivan                 Expires 14 January 2027               [Page 52]
Internet-Draft                    raAE                         July 2026

   read   old_tag_i
   new_nonce_metadata_i, new_ct_i, new_tag_i
          = RewriteSegment(i, is_final, empty, new_P_i)
   remove(i, old_tag_i)                    (count n_seg unchanged)
   add(i, new_tag_i)
   snapshot      = snapshot()
   store  new_nonce_metadata_i, new_ct_i, new_tag_i, snapshot

   In derived nonce mode the nonce for segment i is deterministic, so a
   rewrite reuses the same nonce under a key whose derivation context is
   identical.  This is safe because a mutable profile that selects
   derived nonce mode requires an MRAE AEAD (AES-256-GCM-SIV).  MRAE
   AEADs are designed to tolerate nonce reuse, degrading to equality
   leakage rather than plaintext recovery, within the per-segment
   rewrite limit in Section 5.9.5.  The snapshot update proceeds
   identically.

   The stored snapshot cannot be validated during an in-place rewrite.
   Validation is SnapVerify (Section 4.7), which recomputes the
   authenticator from every segment's tag and so requires reading all
   tags.  During a rewrite the application trusts the stored snapshot
   state, applies remove and add for the one changed segment, and
   produces the new value with snapshot().  Because it trusts that
   state, the writer MUST take it from locally trusted storage or run
   SnapVerify first.  Otherwise a storage adversary can roll the stored
   snapshot back and have the writer rebuild on it, laundering the
   rollback into a snapshot that verifies going forward (Section 6.6).
   Full validation is a separate operation that an application performs
   when it has access to all tags (for example, on a full read or an
   integrity audit).

4.9.2.2.  Changing Message Length

   An application that holds the CEK MAY change a stored message's
   length in either direction: appending new segments, or truncating to
   a prefix.  The two directions share one shape.  Exactly one retained
   segment, the old or the new terminal one, is re-encrypted to move the
   finality bit, with none re-encrypted when appending from the empty
   object or truncating to it.  Every other changed segment is removed
   or added by tag without re-encryption, and the snapshot is updated
   over the new count (Figure 6).  Both reuse the existing salt: an
   application extending an existing message MUST reuse the salt from
   the object it extends.  A fresh salt would change every payload
   schedule output and force re-encryption of the retained segments.

Sullivan                 Expires 14 January 2027               [Page 53]
Internet-Draft                    raAE                         July 2026

 Both directions share one shape (no terminal re-mark for the
 empty object):

   1. re-mark the single terminal segment   remove + add (new count)
   2. update the boundary segments by tag    remove or add  (each)
   3. update the snapshot                    snapshot()

   append   (n_seg' = n_seg + k):
      terminal = old final n_seg-1  (is_final 1 -> 0); none if n_seg = 0
      add IN   the new segments       n_seg..n_seg+k-1
   truncate (n_seg' = m):
      terminal = new final m-1      (is_final 0 -> 1); none if m = 0
      remove   the discarded segments m..n_seg-1

                     Figure 6: Append and Truncate

 re-mark the terminal segment t:
    append:    t = n_seg - 1    is_final: 1 -> 0
    truncate:  t = m - 1        is_final: 0 -> 1

 if a terminal segment t exists (n_seg >= 1 for append, m >= 1 for
 truncate):
    read   salt, payload schedule, snapshot value, segment t
           (truncate also reads old_tag_j for discarded j in m..n_seg-1)
    P_t    = plaintext of segment t       (decrypt, or use if held)
    meta, ct, new_tag_t
           = RewriteSegment(t, is_final_t, empty, P_t)
    under the old count:  remove(t, old_tag_t)
 else:
    (append from empty: no old terminal; the appended loop below
     marks the last new segment is_final = 1.  truncate to m = 0:
     no segment remains, so nothing is re-marked.)
 truncate:  for j in m .. n_seg-1:  remove(j, old_tag_j)

 n_seg' = n_seg+k (append) or m (truncate)
 set_length(n_seg')

 if a terminal segment t exists:  add(t, new_tag_t)
 append:    for j in n_seg .. n_seg+k-1:
               meta_j, ct_j, tag_j
                   = EncryptSegment(j, is_final_j, empty, P_j)
               add(j, tag_j)
            (is_final_j = 1 only for the last segment)

 snapshot      = snapshot()
 store  segment t and snapshot; append adds the new segments,
        truncate drops segments m..n_seg-1

Sullivan                 Expires 14 January 2027               [Page 54]
Internet-Draft                    raAE                         July 2026

   Changing message length re-encrypts the terminal segment, except when
   truncating to the empty object (m = 0), where no segment remains.  A
   caller that cannot recover that segment's plaintext cannot do it.

   Under the Incremental interface each add and remove takes the
   segment's stored tag, which already encodes that segment's is_final
   bit, so an implementation never tracks finality separately.

   A botched Incremental update corrupts the snapshot authenticator's
   state silently, surfacing only at a later SnapVerify.  An
   implementation MAY run SnapVerify over the new segment set
   immediately after a length change to catch this.  A botched update
   yields a value that the from-scratch recomputation will not match,
   turning a silent corruption into a caught error.  A Recompute-on-
   change authenticator recomputes the snapshot from the stored per-
   segment leaves on every update, so it has no analogous silent-
   corruption failure mode.

   Both operations count against the same per-CEK AEAD usage budget as
   initial encryption (Section 5.9).  Each re-encrypts one terminal
   segment, except a truncate to the empty object (m = 0), which re-
   encrypts none.  In derived nonce mode the nonce for the re-marked
   segment is fixed by its index and finality bit.  Re-marking flips the
   finality bit, so this nonce differs from the segment's previous one,
   but repeated length changes can make the same (index, finality bit)
   pair recur, and only an MRAE AEAD tolerates the resulting nonce reuse
   (Section 4.5).

   Applications that do not need length changes MAY simply forbid them.
   The salt-reuse requirement applies only when a length change is
   performed.  An application that re-encrypts the entire content can
   generate a fresh salt and is not bound by the constraints in this
   section.

   Test vectors for the SEAL profile are provided in Appendix F, and the
   serialization layouts are in Section 4.11.

4.10.  SEAL Suites

   This section lists example algorithm suites for the SEAL
   construction.  SEAL's concrete framing and procedures are defined in
   Section 4.

Sullivan                 Expires 14 January 2027               [Page 55]
Internet-Draft                    raAE                         July 2026

4.10.1.  Algorithms

   SEAL defines the following AEAD algorithms.  Each has a code point, a
   key size, a nonce size, a default nonce mode, and an epoch_length
   range.  The key size Nk is 16 octets for AES-128-GCM and 32 octets
   for the other suites.  All use a 16-octet authentication tag (Nt =
   16).

    +===================+=========+====+====+=========+==============+
    | Algorithm         | aead_id | Nk | Nn | default | epoch_length |
    |                   |         |    |    | mode    |              |
    +===================+=========+====+====+=========+==============+
    | AES-128-GCM       | 0x0001  | 16 | 12 | random  | 0 to 63      |
    |                   |         |    |    |         | (default 0)  |
    +-------------------+---------+----+----+---------+--------------+
    | AES-256-GCM       | 0x0002  | 32 | 12 | random  | 0 to 63      |
    |                   |         |    |    |         | (default 0)  |
    +-------------------+---------+----+----+---------+--------------+
    | ChaCha20-Poly1305 | 0x001D  | 32 | 12 | random  | 0 to 63      |
    |                   |         |    |    |         | (default 0)  |
    +-------------------+---------+----+----+---------+--------------+
    | AES-256-GCM-SIV   | 0x001F  | 32 | 12 | derived | 0 to 63      |
    |                   |         |    |    |         | (default 0)  |
    +-------------------+---------+----+----+---------+--------------+
    | AEGIS-256         | 0x0021  | 32 | 32 | random  | 63           |
    +-------------------+---------+----+----+---------+--------------+
    | AEGIS-256X2       | 0x0024  | 32 | 32 | random  | 63           |
    +-------------------+---------+----+----+---------+--------------+

                      Table 10: SEAL AEAD algorithms

   The aead_id values are the unsigned 16-bit code points from the IANA
   AEAD Algorithms Registry ([RFC5116]), encoded as uint16(id).  The
   registry's textual identifiers are, in table order, AEAD_AES_128_GCM,
   AEAD_AES_256_GCM, AEAD_CHACHA20_POLY1305, AEAD_AES_256_GCM_SIV,
   AEAD_AEGIS256, and AEAD_AEGIS256X2.  AES-128-GCM and AES-256-GCM are
   specified in [NIST-SP-800-38D], ChaCha20-Poly1305 in [RFC8439], AES-
   256-GCM-SIV in [RFC8452], and AEGIS-256 and AEGIS-256X2 in
   [I-D.irtf-cfrg-aegis-aead].  The AEGIS code points 0x0021 and 0x0024
   are early allocations that firm up when that I-D is published as an
   RFC.

   The 96-bit-nonce AEADs (AES-128-GCM, AES-256-GCM, ChaCha20-Poly1305)
   and the MRAE AES-256-GCM-SIV (in derived nonce mode) rotate the
   segment key and may set epoch_length anywhere in 0 to 63.  The 256-
   bit-nonce AEADs (AEGIS-256, AEGIS-256X2) use a flat key (epoch_length
   = 63, one epoch key covering every segment).  Section 5.9.6 gives the
   per-suite budgets that fix these ranges.

Sullivan                 Expires 14 January 2027               [Page 56]
Internet-Draft                    raAE                         July 2026

   The default nonce_mode column gives each AEAD's mode in the mutable
   profile, SEAL-RW-v1.  The immutable profile, SEAL-RO-v1, instead
   pairs a derived nonce with any of these AEADs, because its write-once
   rule keeps every derived nonce unique and needs no MRAE AEAD
   (Section 4.10.2).

   SEAL permits the KDF cipher suites in Table 11, identified by entries
   from the HPKE KDF Registry ([RFC9180] Section 7.2 and
   [I-D.ietf-hpke-pq]):

     +========+================+===============================+====+
     | kdf_id | Name           | Construction                  | Nh |
     +========+================+===============================+====+
     | 0x0001 | HKDF-SHA-256   | Two-step HKDF-Extract+Expand  | 32 |
     +--------+----------------+-------------------------------+----+
     | 0x0002 | HKDF-SHA-384   | Two-step HKDF-Extract+Expand  | 48 |
     +--------+----------------+-------------------------------+----+
     | 0x0003 | HKDF-SHA-512   | Two-step HKDF-Extract+Expand  | 64 |
     +--------+----------------+-------------------------------+----+
     | 0x0013 | TurboSHAKE-256 | One-step XOF (absorb+squeeze) | 64 |
     +--------+----------------+-------------------------------+----+

                Table 11: Permitted SEAL KDF Cipher Suites

   HKDF-SHA-256 is specified in [RFC5869].  TurboSHAKE-256 is the
   extendable-output mode of the Keccak-p permutation family, specified
   in [RFC9861] and selected by [I-D.ietf-hpke-pq] for the HPKE KDF
   Registry.  The kdf_id values are the unsigned 16-bit code points from
   the HPKE KDF Registry, encoded as uint16(id).  Nh and
   commitment_length equal the KDF's primitive output size, 32 octets
   for HKDF-SHA-256, 48 for HKDF-SHA-384, and 64 for HKDF-SHA-512 and
   TurboSHAKE-256.  HKDF-SHA-256 is the baseline KDF.  The others are
   optional alternatives.

   The two-step Extract and Expand are HKDF-Extract and HKDF-Expand.
   The one-step XOF uses TurboSHAKE256 with D = 0x1F, the Derive
   convention's domain-separator byte, which an implementation MUST NOT
   change.

   The snapshot authenticator is selected by snap_id:

Sullivan                 Expires 14 January 2027               [Page 57]
Internet-Draft                    raAE                         July 2026

     +=========+======================+=============================+
     | snap_id | Name                 | Snapshot value              |
     +=========+======================+=============================+
     | 0x0000  | none                 | none produced               |
     +---------+----------------------+-----------------------------+
     | 0x0001  | masked multiset hash | snapshot, per Section 4.7   |
     +---------+----------------------+-----------------------------+
     | 0x0002  | digest transcript    | snapshot, per Section 4.7.5 |
     +---------+----------------------+-----------------------------+
     | 0x0003  | epoch digest tree    | snapshot, per Section 4.7.6 |
     +---------+----------------------+-----------------------------+

                  Table 12: SEAL Snapshot Authenticators

   snap_id = 0x0000 selects no snapshot authenticator: no snapshot value
   is produced and a reader relies on per-segment authentication alone.
   An immutable (write-once) profile enforces snap_id = 0x0000, 0x0002,
   or 0x0003.  A profile that supports rewrite requires a snapshot
   authenticator (snap_id 0x0001, 0x0002, or 0x0003), so every
   rewritable object carries whole-object integrity (Section 4.9.2).
   Every rewrite re-publishes the snapshot value, at a cost that depends
   on the selected authenticator (Section 4.10.2.1).

   The nonce mode is carried explicitly by nonce_mode:

                         +============+=========+
                         | nonce_mode | Name    |
                         +============+=========+
                         | 0x00       | random  |
                         +------------+---------+
                         | 0x01       | derived |
                         +------------+---------+

                        Table 13: SEAL Nonce Modes

   The two constructions are defined in Section 4.5.3.

   The supported maximum segment sizes are 16384 and 65536 octets.  Both
   values are powers of two and at least 4096 octets.  The 16384-octet
   size aligns to 16 KiB memory pages (for example, on Apple Silicon).
   The 65536-octet size aligns to 64 KiB (four 16 KiB pages).

   The aad_label is "SEAL-DATA".  Under snap_id = 0x0001 the snapshot
   authenticator is the masked multiset hash (Section 4.7), with
   wrapped_acc and the snapshot tag each an Nh-octet value, so Na =
   2*Nh.

Sullivan                 Expires 14 January 2027               [Page 58]
Internet-Draft                    raAE                         July 2026

4.10.2.  Composing a SEAL Suite

   A SEAL suite fixes an AEAD and a KDF (Table 10, Table 11), a maximum
   segment size, a snapshot authenticator (snap_id, Table 12), a nonce
   mode (nonce_mode, Table 13), and an epoch length.  The protocol_id
   identifies the parameters a profile fixes, and only certain
   (nonce_mode, snap_id) tuples are valid under each one.  This document
   defines two named profiles, SEAL-RW-v1 and SEAL-RO-v1.

   A profile's payload_info MUST carry the full parameter context
   affecting key derivation, AEAD operations, AAD construction, and
   nonce construction, so that the commitment (Section 4.6) binds that
   context.

   SEAL-RW-v1 is the mutable profile (read-write).  It requires a
   snapshot authenticator, admitting snap_id 0x0001 (the masked multiset
   hash), 0x0002 (the digest transcript), or 0x0003 (the epoch digest
   tree), and it permits a random nonce or a derived nonce with an MRAE
   AEAD.  It supports rewrite, extend, and truncate, and carries SEAL's
   snapshot machinery per the selected snap_id.  Under 0x0001 that is
   the accumulator with its mask; under 0x0002 the recomputed snapshot
   value over the leaf list; under 0x0003 the epoch-heads region and the
   recomputed snapshot value.  All three include SnapVerify.
   Unauthorized truncation surfaces at two points: the terminal finality
   check of Section 4.9.1.2, and SnapVerify over the complete segment
   set, which also binds the segment count.

   SEAL-RO-v1 is the immutable profile (read-only).  Here "immutable"
   names the writer's write-once discipline, not a guarantee of tamper
   evidence.  The write-once discipline follows from the nonce mode.
   SEAL-RO-v1 selects a derived nonce and works with any AEAD including
   a non-MRAE one, so a rewrite would repeat the segment's derived nonce
   with the consequences given in Section 6.2.  An encryptor under SEAL-
   RO-v1 therefore MUST NOT rewrite a segment once it has been written.
   The profile admits snap_id 0x0000 (no snapshot authenticator), 0x0002
   (the digest transcript, Section 4.7.5), or 0x0003 (the epoch digest
   tree, Section 4.7.6), and the choice does not affect the write-once
   discipline.  A protocol that needs rewrites picks SEAL-RW-v1 instead,
   per Section 4.10.2.1 and Section 4.5.3.

   SEAL-RO-v1 provides per-segment confidentiality and integrity,
   binding the segment index and the finality bit, and key commitment
   through the commitment field.  At snap_id 0x0000 it has no snapshot
   or whole-object integrity: that, when needed, comes from the digest
   transcript at snap_id 0x0002 or 0x0003, from the snapshot in the
   mutable profile, or from a layer above SEAL.  At snap_id 0x0002 or
   0x0003 the digest transcript binds the whole segment set, and a
   consuming protocol that authenticates the snapshot value obtains per-

Sullivan                 Expires 14 January 2027               [Page 59]
Internet-Draft                    raAE                         July 2026

   segment origin authentication (Section 5.8.2).  Truncation detection
   at snap_id 0x0000 rests on the finality bit alone and surfaces only
   when the highest-indexed present segment verifies under is_final = 1
   (Section 4.9.1.2), so a consumer of streamed plaintext has no
   completeness guarantee before that terminal check.  At 0x0002 and
   0x0003, SnapVerify additionally binds the count.

     +=============+============+=================+=================+
     | protocol_id | nonce_mode | snap_id         | mutability      |
     +=============+============+=================+=================+
     | SEAL-RW-v1  | random or  | 0x0001, 0x0002, | rewrite/extend/ |
     |             | derived    | or 0x0003       | truncate        |
     +-------------+------------+-----------------+-----------------+
     | SEAL-RO-v1  | derived    | 0x0000, 0x0002, | write-once      |
     |             |            | or 0x0003       |                 |
     +-------------+------------+-----------------+-----------------+

                         Table 14: SEAL profiles

   An encryptor MUST set payload_info to a (nonce_mode, snap_id) tuple
   that is valid for its protocol_id, and a decryptor MUST reject any
   object whose tuple is not.  A derived nonce under SEAL-RW-v1 requires
   an MRAE AEAD.  SEAL-RO-v1 admits any AEAD because the write-once rule
   keeps each derived nonce unique.

4.10.2.1.  Choosing a Profile

   Pick the profile from how the stored content changes after it is
   first written.

   SEAL-RW-v1 (mutable) fits content updated in place: editable files,
   mutable object stores, and append-or-truncate logs.  It admits any of
   snap_id 0x0001, 0x0002, or 0x0003, so a reader detects tampering with
   the current segment set as a whole: any added, dropped, reordered, or
   re-marked segment, a same-index rollback, or a count change
   (Section 5.2.3).  The masked multiset hash (0x0001) updates in O(1)
   per rewrite; the digest transcript (0x0002) recomputes over the full
   leaf list; the epoch digest tree (0x0003) recomputes one epoch head
   and the snapshot from two aligned metadata reads.  Whole-object
   freshness beyond replay still comes from a layer above SEAL
   (Section 6.6).  A derived nonce under this profile requires an MRAE
   AEAD, while a random nonce works with any of the suites.

Sullivan                 Expires 14 January 2027               [Page 60]
Internet-Draft                    raAE                         July 2026

   SEAL-RO-v1 (immutable) fits write-once content: archives, backups,
   content-addressed blobs, and write-once media.  It stores no per-
   segment nonce and works with any AEAD, so it is the smaller and
   simpler choice when content is never rewritten.  Whole-object
   integrity, if needed, comes from the digest transcript (snap_id
   0x0002, Section 4.7.5) or the epoch digest tree (snap_id 0x0003,
   Section 4.7.6), or a layer above SEAL.

   Section 5.9.6 covers epoch_length selection, and Section 4.10.1
   covers the per-AEAD trade-offs that further narrow the suite.

4.11.  Serialization Layouts

   Three serialization layouts, linear, aligned, and split, let a
   consuming protocol store raAE output.  The parameterized SEAL
   construction mandates none of them.  Each named instantiation binds
   one, and the consuming protocol pins the remaining details
   (Section 4.12).  The commitment field in every layout is
   commitment_length octets (Section 4.2.3).  The figures annotate it
   with its default, commitment_length = Nh.

4.11.1.  Linear Layout

   In a linear layout the salt, commitment, snapshot value, and segment
   data appear in sequence.  The salt comes first because it is needed
   to derive all payload schedule values.  The commitment follows so a
   reader can reject a wrong key before reading any segment data.  The
   snapshot value (Na octets), the configured authenticator's output,
   precedes the segment data so a streaming reader has it before the
   segments and can check it once all are read.  Segments then follow in
   index order.

   When the object has at least one segment, that final segment carries
   is_final = 1.  An empty object (n_seg = 0) has zero segments, so no
   segment is final.

   +------+------------+---------------+----------+-----+----------+
   | salt | commitment | snapshot      | segment  | ... | segment  |
   | (32) | (Nh)       |     (Na)      |  0 data  |     | n_seg-1  |
   |      |            |               | [nonce]  |     | [nonce]  |
   |      |            |               | ct + tag |     | ct + tag |
   +------+------------+---------------+----------+-----+----------+

                          Figure 7: Linear Layout

Sullivan                 Expires 14 January 2027               [Page 61]
Internet-Draft                    raAE                         July 2026

   Brackets mark the nonce, which precedes the ciphertext and tag for a
   segment and is stored only in random nonce mode.  In derived nonce
   mode (AES-256-GCM-SIV) the nonce is recomputed from the key schedule,
   so no nonce is stored.

   A streaming reader recovers segment boundaries from the segment
   lengths.  Because a segment MAY be shorter than segment_max
   (Section 2), a linear layout that is to be read as a stream MUST keep
   every non-final segment at the full segment_max, leaving only the
   final segment short.  A reader then finds each boundary at the fixed
   segment length.  A layout that stores shorter interior segments MUST
   record their lengths so the reader can locate each segment.

   Linear layout supports streaming writes.  A writer emits the salt,
   commitment, and a placeholder for the snapshot value, then streams
   segments.  After all segments are written the writer seeks back to
   the snapshot position and writes the final value in place.

4.11.2.  Aligned Layout

   In an aligned layout the ciphertext segments occupy slots aligned to
   segment_max, so a reader can seek to any segment with page-aligned I/
   O.  An arbitrary-length prefix, which the consuming protocol uses for
   its own machinery and which raAE does not specify, comes first.  The
   raAE header follows, and the ciphertext follows the header.

   +--------------------------------------------+
   | prefix (consuming protocol machinery,      |
   | arbitrary length; not specified here)      |
   +--------------------------------------------+
   | header: salt, commitment, snapshot,        |
   | per-segment metadata (broken out below)    |
   +--------------------------------------------+
   | leading slot: either the first ciphertext  |
   | segment (< B), or zero padding to a        |
   | multiple of B                              |
   +============================================+
   | ciphertext segment            (= B)        |
   +--------------------------------------------+
   | ...                                        |
   +--------------------------------------------+
   | final ciphertext segment      (<= B)       |
   +--------------------------------------------+

                          Figure 8: Aligned Layout

Sullivan                 Expires 14 January 2027               [Page 62]
Internet-Draft                    raAE                         July 2026

   Offsets are measured from index 0, the start of the prefix.  Let B =
   segment_max and let H be the offset at which the ciphertext begins
   (the prefix length plus the header length).  The leading slot, from H
   up to the first segment boundary M * B, is filled in one of two ways,
   chosen before writing:

   *  No padding: the first ciphertext segment occupies the slot, with M
      = ceil(H / B).  It is shorter than B and ends on the boundary M *
      B.

   *  Padding: the slot is zero-padded to a multiple of B, with M >=
      ceil(H / B) (the next boundary, or a larger multiple to reserve
      whole segment slots for append headroom).

   From M * B onward every ciphertext segment begins at a multiple of B
   and is a full B octets, with the final segment at most B.  H, M, and
   the first-segment length all follow from the prefix and header sizes,
   so a writer computes them before emitting any ciphertext.

   The header holds the salt, the commitment, the snapshot value, an
   optional epoch-heads region, and one metadata entry per segment:

 +------+------------+----------+---------------+----------------------+
 | salt | commitment | snapshot | [epoch heads] | per-segment metadata |
 | (32) |    (Nh)    |   (Na)   |    (E * Nh)   |  (n_seg * meta_len)  |
 +------+------------+----------+---------------+----------------------+

                    Figure 9: Aligned Layout Header

   The bracketed epoch-heads region is present only under the epoch
   digest tree (snap_id 0x0003, Section 4.7.6).  It holds the E epoch
   heads, E * Nh octets.

   Each metadata entry holds the segment's stored nonce and its AEAD
   tag:

   +------------+------------+-----------+
   | [nonce(i)] | [LH(ct_i)] |   tag(i)  |
   |    (Nn)    |    (Nh)    | (Nt = 16) |
   +------------+------------+-----------+

                         Figure 10: Metadata Entry

   The bracketed nonce is present only in random nonce mode, and the
   bracketed ciphertext digest LH(ct_i) only under the digest transcript
   (snap_id 0x0002, Section 4.7.5) and the epoch digest tree (snap_id
   0x0003, Section 4.7.6).  The tag is always present, Nt = 16 octets in
   every SEAL suite (Table 10).  The fields are stored in leaf order:

Sullivan                 Expires 14 January 2027               [Page 63]
Internet-Draft                    raAE                         July 2026

   under a derived nonce (Np = 0) the metadata entry is exactly the
   leaf, LH(ct_i) || tag(i), so an implementation folds the stored bytes
   directly.  Each metadata entry holds the Np-octet presented nonce and
   the Nt-octet tag, so meta_len = Np + Nt octets, plus Nh under the
   digest transcript or the epoch digest tree.  A random-nonce entry
   sets Np = Nn and is Nn + Nt octets.  A derived-nonce entry recomputes
   the nonce from the key schedule, so Np = 0 and the entry is Nt
   octets.  The per-suite values, for a metadata entry with no stored
   leaf (snap_id 0x0000 or 0x0001), are:

           +===========================+============+==========+
           | AEAD                      | nonce mode | meta_len |
           +===========================+============+==========+
           | AES-128-GCM, AES-256-GCM, | random (Nn | 28       |
           | ChaCha20-Poly1305         | = 12)      |          |
           +---------------------------+------------+----------+
           | AEGIS-256, AEGIS-256X2    | random (Nn | 48       |
           |                           | = 32)      |          |
           +---------------------------+------------+----------+
           | AES-256-GCM-SIV           | derived    | 16       |
           +---------------------------+------------+----------+

                   Table 15: Metadata entry size by AEAD

   The n_seg entries total n_seg * meta_len octets, so the whole header
   size is:

   header_size = 32 + commitment_length + Na + n_seg * meta_len

   With the default commitment_length = Nh, this specializes by
   authenticator:

   masked multiset hash (Na = 2 * Nh):
       32 + 3 * Nh + n_seg * meta_len
   digest transcript (Na = Nh):
       32 + 2 * Nh + n_seg * meta_len
   epoch digest tree, adds the epoch-heads region:
       32 + 2 * Nh + E * Nh + n_seg * meta_len

   A reader verifies the commitment and the snapshot value from the
   header alone, then seeks to any segment using these offsets.  Because
   the authenticator's per-segment inputs live in the metadata entries
   (each tag at snap_id 0x0001, each stored leaf at 0x0002 and 0x0003),
   a reader authenticates the whole object's snapshot from the header
   without reading or streaming any ciphertext.  At 0x0002 it MUST then
   check each leaf against the segment it reads (Section 4.7.5).  This
   supports efficient random-access reads.

Sullivan                 Expires 14 January 2027               [Page 64]
Internet-Draft                    raAE                         July 2026

   Under the epoch digest tree a reader fetches the snapshot value and
   the epoch-heads region together, then reads one epoch's leaf run to
   check a segment.

4.11.3.  Split Layout

   A split layout separates the ciphertext from the metadata into two
   streams that grow independently.  The data stream holds the
   ciphertext segments, each a full segment_max except the last.
   Segment i is at offset i * B.  The metadata stream holds the salt,
   the commitment, the n_seg per-segment metadata entries (each meta_len
   octets, broken out in Figure 10), and the snapshot value last.  The
   authenticator's per-segment inputs live in the metadata stream: tags
   at snap_id 0x0001 (Section 4.7.4) and stored leaves at 0x0002 and
   0x0003.  A reader therefore authenticates the snapshot by reading
   only the metadata stream, never the data stream that holds the
   ciphertext.  Under the epoch digest tree (snap_id 0x0003) the
   metadata stream also carries the E epoch heads, an additional region
   of E * Nh octets.  Section 4.11.2 gives the region's byte layout.

   data stream:

   +--------------------+
   | seg 0 ct    (= B)  |
   +--------------------+
   | seg 1 ct    (= B)  |
   +--------------------+
   | ...                |
   +--------------------+
   | seg n_seg-1 (<= B) |
   +--------------------+

   metadata stream:

   +------+------------+------------+-----+------------+---------------+
   | salt | commitment |   meta_0   | ... | meta_last  | snapshot      |
   | (32) |    (Nh)    | (meta_len) |     | (meta_len) |     (Na)      |
   +------+------------+------------+-----+------------+---------------+

                          Figure 11: Split Layout

   Because neither stream embeds the other, both grow by appending.
   Extending a message appends one ciphertext segment to the data
   stream, appends one metadata entry to the metadata stream, and
   rewrites the trailing snapshot value.  Truncating drops the tail of
   each stream and rewrites the snapshot value.  Neither operation
   shifts an existing ciphertext segment, which the in-place aligned
   layout cannot avoid once the header grows.

Sullivan                 Expires 14 January 2027               [Page 65]
Internet-Draft                    raAE                         July 2026

4.11.4.  Immutable-Profile Layouts

   Under an immutable profile (SEAL-RO-v1, derived nonce), stored fields
   drop out of the layouts above.  Each nonce is recomputed from the key
   schedule, so Np = 0 and no nonce is stored.  At snap_id 0x0000 no
   snapshot authenticator runs, so the snapshot value also drops (Na =
   0), and a metadata entry is the Nt-octet tag alone.  The linear
   layout reduces to the salt, the commitment, and the per-segment
   ciphertext and tags.  The aligned and split header sizes are:

   snap_id 0x0000 (Na = 0):
       32 + commitment_length + Nt * n_seg
   snap_id 0x0002 (Na = Nh, adds the leaf):
       32 + commitment_length + Nh + (Nt + Nh) * n_seg
   snap_id 0x0003 (adds the epoch-heads region):
       32 + commitment_length + Nh + E * Nh + (Nt + Nh) * n_seg

   At snap_id 0x0002 each aligned or split metadata entry also carries
   the segment's leaf (Section 4.7.5).  At snap_id 0x0003 the header
   also carries the epoch-heads region (Section 4.7.6).  In the aligned
   layout every ciphertext segment begins at a multiple of segment_max,
   so a reader seeks to any segment by arithmetic on its index and
   verifies it from its own tag.

4.12.  SEAL Named Instantiations

   SEAL has many parameters (Section 4.2).  A relying protocol that does
   not want to choose them all can cite one of the named instantiations
   in the table below.  Each row fixes a profile, a segment size, the
   nonce mode, a snapshot authenticator, and one of the serialization
   layouts of Section 4.11, leaving the cipher suite (an aead_id and a
   kdf_id from Section 4.10) to the referencing protocol.  Each
   instantiation applies to a cipher suite the same way, so a protocol
   writes SEAL-simple(aead_id, kdf_id), SEAL-disk(aead_id, kdf_id), and
   so on, to obtain a complete raAE scheme.

Sullivan                 Expires 14 January 2027               [Page 66]
Internet-Draft                    raAE                         July 2026

   +===========+=======+=============+==========+=====+=======+=======+
   |Name       |Profile| segment_max |nonce_mode|epoch|snap_id|layout |
   +===========+=======+=============+==========+=====+=======+=======+
   |SEAL-simple|SEAL-  | 65536       |derived   |32   |0x0000 |linear |
   |           |RO-v1  |             |          |     |       |       |
   +-----------+-------+-------------+----------+-----+-------+-------+
   |SEAL-      |SEAL-  | 65536       |derived   |10   |0x0003 |aligned|
   |attachment |RO-v1  |             |          |     |       |       |
   +-----------+-------+-------------+----------+-----+-------+-------+
   |SEAL-      |SEAL-  | 65536       |derived   |10   |0x0002 |aligned|
   |attachment-|RO-v1  |             |          |     |       |       |
   |small      |       |             |          |     |       |       |
   +-----------+-------+-------------+----------+-----+-------+-------+
   |SEAL-      |SEAL-  | 65536       |random    |16   |0x0001 |linear |
   |editable   |RW-v1  |             |          |     |       |       |
   +-----------+-------+-------------+----------+-----+-------+-------+
   |SEAL-memory|SEAL-  | 16384       |random    |16   |0x0001 |aligned|
   |           |RW-v1  |             |          |     |       |       |
   +-----------+-------+-------------+----------+-----+-------+-------+
   |SEAL-disk  |SEAL-  | 16384       |random    |16   |0x0001 |split  |
   |           |RW-v1  |             |          |     |       |       |
   +-----------+-------+-------------+----------+-----+-------+-------+
   |SEAL-      |SEAL-  | 16384       |derived   |16   |0x0001 |aligned|
   |compact    |RW-v1  |             |          |     |       |       |
   +-----------+-------+-------------+----------+-----+-------+-------+

                   Table 16: SEAL named instantiations

   Each instantiation is named for its primary use case, described
   below.  The snap_id column selects the snapshot authenticator within
   the profile's admitted set (Section 4.10.2).

   Every instantiation uses commitment_length = Nh and a fresh 32-octet
   salt per object (Section 4.9.1.1).  The epoch_length is the value in
   the table.  The write-once SEAL-simple performs no rewrites, so it
   takes a larger epoch (32).  SEAL-attachment, also write-once, takes
   epoch_length 10 for the epoch digest tree's grouping (Table 7).
   SEAL-attachment-small takes the same epoch_length as SEAL-attachment
   for the same aligned per-segment use case.  The four mutable schemes
   take a conservative 16 that leaves the per-epoch-key budget headroom
   for rewrites, which reuse the epoch key (Section 5.9).  A 256-bit-
   nonce suite (AEGIS-256, AEGIS-256X2) uses a flat key (epoch_length
   63) regardless of the row (Table 10), and a referencing protocol MAY
   override the epoch.  Each instantiation binds the layout named in its
   row (Section 4.11).  The layout fixes field order and placement, and
   the consuming protocol pins the remaining serialization details, such
   as the aligned layout's prefix and leading slot, for byte-level
   interoperability.  SEAL-compact is mutable with a derived nonce, so

Sullivan                 Expires 14 January 2027               [Page 67]
Internet-Draft                    raAE                         July 2026

   an in-place rewrite reuses that nonce, which requires an MRAE AEAD
   (Section 4.10.2).  The write-once SEAL-simple, SEAL-attachment, and
   SEAL-attachment-small, and the random-nonce SEAL-editable, SEAL-
   memory, and SEAL-disk admit any AEAD.

   SEAL-simple is for write-once content read whole.  It carries no
   snapshot authenticator, because under the immutable profile per-
   segment authentication and the finality bit detect truncation,
   reordering, and tampering on a whole-object read, and write-once
   leaves no earlier version to roll a segment back to (Section 6.6).
   Its linear layout takes the reduced immutable form (Section 4.11.4).

   SEAL-attachment is for write-once content whose segments a reader
   verifies or decrypts individually.  It requires the epoch digest tree
   (snap_id 0x0003, Section 4.7.6): an object under SEAL-attachment MUST
   carry its snapshot value, which binds every segment's ciphertext into
   the two-level fold's Nh-octet snapshot value that holds even against
   holders of the CEK.  Its aligned layout (Section 4.11.2) stores each
   epoch's leaf run in the metadata stream, so a reader verifies one
   segment in two aligned metadata reads plus the segment itself,
   without reading the other segments.  Each of those two reads is one
   segment_max or less for objects up to about 128 GB at the recommended
   parameters (Table 7, Section 5.8.3).

   SEAL-attachment-small is the same shape for small write-once content
   whose segments a reader verifies or decrypts individually, without
   the epoch digest tree's locality benefit.  It uses the flat digest
   transcript (snap_id 0x0002, Section 4.7.5) in the aligned layout.  A
   reader still verifies one segment against the snapshot, but does so
   by reading the full leaf list rather than one epoch's leaf run and
   the list of epoch heads.  That is a sensible tradeoff when the object
   is small enough that the leaf list fits in one read anyway, or when
   the consumer verifies every segment on the way through.

   SEAL-editable is the basic mutable object with whole-object integrity
   from the masked multiset hash (Section 4.7.4), stored in the linear
   layout (Section 4.11.1).  SEAL-memory targets in-memory random access
   with the aligned layout (Section 4.11.2).  SEAL-disk rewrites
   individual segments on stored media, with the split layout
   (Section 4.11.3) holding the headers apart from the segment data so a
   rewrite touches one segment and extension appends to both streams.

   SEAL-compact is the aligned layout with a derived nonce.  Like SEAL-
   memory it stores each segment at a fixed offset for random access,
   but the derived nonce is recomputed rather than stored, so each per-
   segment metadata entry is Nt octets instead of Nn + Nt
   (Section 4.11.2).  Because that region scales with the object,
   removing the stored nonce saves Nn octets per segment, a substantial

Sullivan                 Expires 14 January 2027               [Page 68]
Internet-Draft                    raAE                         July 2026

   part of the metadata for the large random-access objects the aligned
   layout targets.  It reuses the derived nonce on an in-place rewrite,
   so it requires an MRAE AEAD and the rewrite is deterministic.

5.  Security Analysis

   This section states the construction's target security properties,
   the assumptions its components must satisfy, and the operational
   limits on its use, but does not reproduce the formal proofs.  The
   division of labor is deliberate.  The ra-ROR and ra-CMT notions are
   taken unchanged from [FLRR25], whose proofs apply to SEAL's
   realization of the base interface.  Snapshot integrity is the one
   notion this document defines.  Appendix C.5 argues its bound, and
   integrating that argument into a combined proof is deferred to
   [SEALPROOFS], the companion formal write-up, in preparation, that
   this document cites for every deferred proof.  The limits of
   Section 5.9 are operational deployment bounds, not proof obligations.
   The security notions are defined in Section 5.2.

5.1.  Operational Summary

   The construction provides the following properties against an
   adversary that observes ciphertexts, tampers with stored segments or
   their order, and attempts decryption with chosen keys, but does not
   know the CEK or per-content salt:

   Confidentiality:  The construction targets ra-ROR (Section 5.2.1):
      the adversary learns nothing about the plaintext beyond what the
      underlying AEAD already permits.  The adversary observes the
      message's segment count and the per-segment lengths from the wire
      format.  Segment boundary metadata is not confidential.

   Integrity:  A reader that runs snapshot verification, including its
      index-set check over the correct segment count n_seg
      (Section 4.7.1), detects any added, dropped, reordered, or re-
      marked segment, including same-index rollback to an older valid
      segment.  Per-segment AEAD verification alone detects modification
      of individual segments and relocation of a segment to a different
      index, but reverting a segment to a previous version at the same
      index is not detected.  Snapshot verification realizes the
      snapshot integrity notion of Section 5.2.3.  Replacement of the
      entire message with a previous valid version under the same CEK is
      not detected, even when performing snapshot verification.
      Rollback resistance at the message level is the application's
      responsibility (Section 6.6).

   Context commitment:  A reader that verifies the commitment

Sullivan                 Expires 14 January 2027               [Page 69]
Internet-Draft                    raAE                         July 2026

      (Section 4.6) rejects a wrong CEK, parameter context, or global
      associated data before decrypting any segment, per the ra-CMT
      notion (Section 5.2.2).  Commitment to segment positions is out of
      scope.  See Section 5.7 for that gap and its non-normative
      mitigations.

   Rewrite safety:  An individual segment can be rewritten in place
      under the same CEK without re-derivation of other keys, provided
      the per-key AEAD usage budget (Section 5.9) is not exceeded.  A
      rewrite reuses the segment's derived nonce, so in derived nonce
      mode it MUST use an MRAE AEAD (Section 4.5.3.2).  Rewrite safety
      is otherwise a corollary of ra-ROR and snapshot integrity
      (Section 5.3).

   Out of scope:  Atomicity of writes (the application MUST use write-
      ahead logging or copy-on-write); confidentiality of segment count
      or layout; traffic-analysis resistance; side channels in the
      application's storage layer; protection of the CEK before
      encryption or after decryption.

5.2.  Security Notions

   raAE has two security tiers.  The base tier comprises the two notions
   inherited from [FLRR25], ra-ROR and ra-CMT, summarized here.  The
   extension tier adds one notion, snapshot integrity, defined by this
   document.  A construction MAY provide the base tier only.  Advantage
   definitions and proofs for ra-ROR and ra-CMT are in [FLRR25].

5.2.1.  ra-ROR

   ra-ROR (random-access real-or-random) is the joint confidentiality
   and authenticity notion for raAE in a multi-user, nonce-respecting
   setting.  An adversary with adaptive encryption and decryption access
   across many key instances, sessions, and arbitrary positions cannot
   distinguish real ciphertexts and headers from random, and cannot make
   an out-of-context ciphertext segment decrypt.  The game is defined in
   [FLRR25].

   The encryption core of the extension operations is ordinary EncSeg.
   A RewriteSeg produces its replacement ciphertext as a repeated EncSeg
   at the same position, and appending is EncSeg at fresh positions plus
   one RewriteSeg of the old final segment.

Sullivan                 Expires 14 January 2027               [Page 70]
Internet-Draft                    raAE                         July 2026

   In random nonce mode these draw fresh nonces and stay within the
   nonce-respecting ra-ROR game.  In derived nonce mode a rewrite reuses
   the position's derived nonce.  This is a deliberate nonce-repeating
   query.  The underlying MRAE AEAD handles it under the equality-
   leakage relaxation that derived nonce mode documents (Appendix C.6),
   not the unmodified nonce-respecting game.

   These operations also maintain a snapshot value.  That value is
   outside the [FLRR25] syntax.  It is governed not by ra-ROR but by the
   snapshot integrity notion of Section 5.2.3.

5.2.2.  ra-CMT

   ra-CMT (random-access context commitment) is the segment-level
   commitment notion: no single ciphertext segment decrypts successfully
   under two different decryption contexts (key, nonce, global
   associated data, and per-segment associated data).  In the position-
   respecting variant ra-CMT the two contexts share a position.  The ra-
   CMT-p variant lets the adversary choose the positions freely.
   Because the scheme is random access, committing one segment's context
   commits the full ciphertext's context.  Both game variants are
   defined in [FLRR25].

   ra-CMT is distinct from the per-AEAD notions CMT-1 and CMT-4 of
   [RFC9771]: a scheme can achieve ra-CMT through an external commitment
   mechanism even when its underlying AEAD commits to nothing.  The
   Invisible Salamanders attacks ([DGRW18]) show what a non-committing
   AEAD permits, and [ADG22] frames it as a key-commitment failure with
   fixes.

5.2.3.  Snapshot Authentication

   Per-segment AEAD authenticates a segment at its position, but it does
   not authenticate the current object snapshot: which set of segments
   belongs to the current stored object state.  An adversary with write
   access to stored segments can silently substitute an old valid same-
   index segment or delete a segment, and per-segment AEAD verification
   at each presented segment still passes.  (A segment moved to a
   different index fails its AEAD check through the index binding of
   Section 4.4.2.)

   Snapshot authentication covers that set.  The resulting notion,
   snapshot integrity, is the one this document adds.  The construction
   that realizes it is in Section 4.7, its bound is in Section 5.8, and
   the supporting reduction is in Appendix C.5.  A combined formal
   treatment with the ra-ROR framework is in preparation ([SEALPROOFS]).

Sullivan                 Expires 14 January 2027               [Page 71]
Internet-Draft                    raAE                         July 2026

   A construction MAY omit snapshot integrity, providing only the per-
   segment guarantees and ra-CMT.  SEAL provides it under a snapshot
   authenticator (snap_id != 0x0000) and omits it otherwise
   (Section 4.10.2).

5.3.  Relations to Other Notions

   raAE relates to the following established security notions.

   AEAD ([RFC5116]):  raAE generalizes AEAD to multi-segment content
      with arbitrary-order encryption.  A single-segment raAE is an
      AEAD.

   nOAE2 ([Tink]):  nOAE2 is the nonce-based online authenticated
      encryption notion for schemes that encrypt in order and support
      random-access decryption.  Hoang and Shen introduced nOAE2 in
      their analysis of Tink Streaming AEAD ([Tink]), where they also
      show STREAM satisfies it.  ra-ROR is strictly stronger: every ra-
      ROR-secure scheme is nOAE2-secure when used in order, but
      nOAE2-secure schemes can fail when encryption order is arbitrary
      ([FLRR25]).

   Commitment ([RFC9771]):  CMT-1 and CMT-4 ([RFC9771]) are per-AEAD
      commitment notions. ra-CMT is the corresponding notion for
      segmented random-access schemes.  See Section 5.2.2.

   Rewrite safety and extension safety are corollaries of the notions
   above, not additional notions.  Replacing a segment in place is an
   EncSeg at an already-used position together with a snapshot update.
   In random nonce mode it adds no adversary capability beyond ra-ROR
   (Section 5.2.1) and snapshot integrity (Section 5.2.3).  In derived
   nonce mode the rewrite repeats the segment nonce, so it MUST use an
   MRAE AEAD (Section 4.5.3.2), and it adds the equality-leakage
   relaxation that derived nonce mode already documents (Appendix C.6).
   Each rewrite also counts against the per-key AEAD usage budget
   (Section 5.9).

   Appending and truncating likewise add no algorithm (Section 4.9.2.2)
   and no notion.  An adversary's ability to add, drop, reorder, or re-
   mark a terminal segment is exactly the snapshot integrity adversary
   of Section 5.2.3, and detection follows from the index and finality
   binding of Section 4.4.2 together with snapshot verification.
   Rollback of the whole object to an earlier honest (state, snapshot)
   after truncation is the freshness case that snapshot integrity
   excludes (Section 6.6).  The application supplies it.

Sullivan                 Expires 14 January 2027               [Page 72]
Internet-Draft                    raAE                         July 2026

5.4.  Adversary Model and Assumptions

   The adversary model is that of the ra-ROR game (Section 5.2.1,
   [FLRR25]).  In the construction's terms: the adversary can observe
   all ciphertexts and content metadata, tamper with individual segments
   or their ordering, replace the snapshot value, and attempt decryption
   with chosen keys.  The adversary does not know the CEK, any derived
   key, or the per-content salt.  Every bound in this section assumes
   the CEK is secret and uniform: an application MUST supply a CEK that
   is either generated uniformly at random or derived so that it is
   computationally indistinguishable from uniform, such as the output of
   a KDF keyed by a secret.  When multiple messages share a CEK, the
   per-content salt separates payload schedule outputs across messages
   under the PRF assumption.  The multi-message advantage grows linearly
   in the number of messages.  The construction claims the properties
   below against this adversary.

   The persistent state of an raAE object, and the party that owns each
   item, is:

   CEK:  Secret key-management state.  It is the root secret for the
      object.

   salt:  Stored object metadata.  It separates objects under one CEK.

   parameter set:  Profile or object metadata.  It fixes suite, maximum
      segment size, and epoch policy.

   commitment:  Stored object metadata.  It rejects wrong key or
      parameter context before decryption.

   segment metadata:  Storage-format state.  It holds AEAD tags, the
      per-segment nonces in random nonce mode, and the ciphertext-digest
      leaves under the digest transcript.

   snapshot:  Stored object metadata.  It authenticates the segment set
      and segment count.

   external freshness state:  Consuming-protocol state.  It detects
      whole-object rollback.

   The security argument relies on three distinct assumptions about the
   KDF.  For derivations whose input keying material is secret and
   uniformly random, including payload_key, snap_key, nonce_base,
   epoch_key, and the snapshot authenticator's keyed derivations, the
   KDF is assumed to be a multi-user PRF with domain separation by
   protocol_id and label.

Sullivan                 Expires 14 January 2027               [Page 73]
Internet-Draft                    raAE                         July 2026

   For commitment, the function

   Commit(CEK, payload_info, G, L) =
       KDF(protocol_id, "commit", [CEK], [...payload_info, G], L)

   is additionally assumed to be collision resistant over adversarially
   chosen tuples (CEK, payload_info, G), with G empty by default.  PRF
   security alone is not sufficient for this property, because the
   commitment adversary may choose the CEK and context values.

   For the plaintext-bound nonce construction, the plaintext digest step
   targets collision resistance, not PRF security.

   For the HKDF cipher suites (HKDF-SHA-256, HKDF-SHA-384, and HKDF-SHA-
   512), the PRF assumptions are placed on HKDF keyed by the CEK or
   payload_key, and the collision-resistance assumption on the commit
   derivation reduces to the collision resistance of the underlying
   SHA-2 variant (SHA-256, SHA-384, or SHA-512).  Because every SEAL KDF
   output is at most Nh octets, HKDF-Expand makes a single HMAC call per
   derivation.  The extractor-then-PRF analysis of HKDF is in [RFC5869].

   For TurboSHAKE-256 ([RFC9861], the one-step XOF cipher suite defined
   in [I-D.ietf-hpke-pq]), the same PRF and collision-resistance
   assumptions are placed on the XOF as instantiated by that HPKE KDF
   registry entry, which fixes the primitive and its parameters but
   carries no security assumptions of its own.

5.5.  raAE Construction Requirements

   The bounds below rely on properties of the construction's components,
   namely the KDF framing and the snapshot authenticator.  The
   requirements those components MUST satisfy are obligations on the
   raAE construction, not part of the raAE primitive of Section 3.

   A profile selects the concrete framing for every derivation surface:
   the KDF input assembly (Section 4.5), the payload_info construction
   (Section 4.2), the segment AAD (Section 4.4.2), and the snapshot
   authenticator's keyed inputs (Section 4.7).  The requirements below
   are the contract that any such framing MUST meet.  SEAL is one such
   framing (Section 4.3, Section 4.4).

   The per-component requirements any raAE construction must satisfy are
   as follows.

   Pseudorandomness:  KDF outputs MUST be computationally
      indistinguishable from random to an adversary that does not know
      the ikm, assuming the underlying hash or PRF is a secure
      pseudorandom function.

Sullivan                 Expires 14 January 2027               [Page 74]
Internet-Draft                    raAE                         July 2026

   Injectivity:  The construction MUST define the encoding of the tuple
      (protocol_id, label, ikm, info, L) into the underlying primitive
      input, and that encoding MUST be injective: distinct tuples MUST
      map to distinct primitive inputs.  This is a requirement on the
      encoding, not on the KDF.  The KDF need not itself be injective,
      because injectivity is supplied by the encoding placed in front of
      it.  ikm and info MUST be unambiguously separable in the encoded
      input, whatever the number of elements each contains.  The two-
      step form binds ikm and info in its separate Extract and Expand
      inputs.  The one-step form frames ikm and info each as a single
      encode element.  Where the encoding admits inputs too large to
      encode literally, it MAY substitute a fixed-length digest of the
      over-large field.  The encoding is then injective only up to the
      collision resistance of that digest, which the construction MUST
      justify and domain-separate from the literal encoding
      (Section 4.3).

   Label uniqueness:  Each derivation role, including commitment,
      payload key, snapshot authenticator key, nonce base, epoch key,
      segment nonce, hedged key, and the authenticator's internal roles,
      MUST use a distinct label string within each protocol version.

   Protocol binding:  The protocol_id MUST appear as a distinct
      component of the KDF primitive input.  Different application
      protocols using the same AEAD and KDF MUST use different
      protocol_id values to ensure that derived values from one protocol
      cannot be confused with those from another.

   Output length commitment:  The requested output length L MUST be part
      of the KDF primitive input.  This prevents attacks in which an
      adversary attempts to use a truncated version of a longer derived
      output as a valid shorter one.

   Cross-role isolation:  A KDF output for one derivation role, such as
      payload_key, MUST be computationally independent of the output for
      any other role, such as snap_key, even when derived from the same
      CEK and payload_info.  This property follows from label uniqueness
      and PRF security of the underlying primitive.

   Snapshot authenticator:  The construction MUST bind the current
      segment set and the segment count into a public snapshot value
      that an adversary without the authenticator's secret key cannot
      forge for a modified segment set or count, even after observing
      snapshot values and their deltas across rewrites.  How the value
      is computed, and whether a single-segment change updates it in
      place or rebuilds it, is the authenticator's choice.  SEAL's
      snapshot authenticators are in Section 4.7.

Sullivan                 Expires 14 January 2027               [Page 75]
Internet-Draft                    raAE                         July 2026

   SEAL's construction (Section 4) satisfies these requirements.  Its
   KDF (Section 4.3), any of the cipher suites in Table 11 keyed by the
   CEK, is modeled as a pseudorandom function.  Its encode framing
   (Section 4.3) is injective and places protocol_id and the output
   length L in the primitive input, so injectivity, protocol binding,
   and output-length commitment hold.  SEAL's labels (Table 4, the
   masked multiset hash's labels in Section 4.7.4, the digest
   transcript's label "snap_transcript" in Section 4.7.5, the epoch
   digest tree's labels "snap_epoch" and "snap_epoch_root" in
   Section 4.7.6, the plaintext-bound construction's labels in
   Appendix A, and the hedged-randomness label in Appendix B) are
   distinct, giving label uniqueness and, with PRF security, cross-role
   isolation.  SEAL's masked multiset hash (Section 4.7) satisfies the
   snapshot authenticator requirement, with its forgery argument in
   Section 5.8.

   encode is injective.  Among fields of at most 0xFFFE octets each
   frame entry is self-delimiting, so the concatenation parses uniquely
   regardless of the number of arguments, and distinct input tuples
   produce distinct output strings.  For over-large fields the encoding
   is injective only up to the collision resistance of LH, not
   unconditionally: a literal entry carries a length prefix in the range
   0x0000 through 0xFFFE while a digest entry carries the reserved
   prefix 0xFFFF, so a literal entry and a digest entry never collide,
   and a collision between two digest entries reduces to a collision in
   LH.  A 0xFFFF entry is bind-only: it commits to the field but does
   not carry it, so a protocol that must recover a field from the
   encoding MUST keep that field at most 0xFFFE octets.  SEAL's own
   derived and fixed fields, the payload_info elements, the commitment,
   the leaf elements, and the uint-encoded scalars, are all at most
   0xFFFE octets, so for them frame is byte-identical to a plain 2-octet
   length prefix.  The two caller-supplied octet strings, the global
   associated data G (Section 4.2.4) and the per-segment associated data
   A_i, are unbounded: an application that supplies one longer than
   0xFFFE octets has that field bound as 0xFFFF || LH of it.  The digest
   transcript invokes LH directly on each segment's ciphertext,
   digesting it to a fixed Nh-octet value that it concatenates with the
   Nt-octet tag to form leaf(i) before framing it into the transcript
   (Section 4.7.5), so no transcript element is over-large.

   The "raAE-LP-v1" prefix (the HKDF-Extract salt in the two-step class,
   a fixed input prefix in the one-step class) is distinct from every
   protocol_id and label, so LH inputs are domain-separated from encode
   inputs.  LH is a shared collision-resistant digest rather than a per-
   protocol separator: the same over-large field yields the same LH
   value in every protocol that reuses this combiner.  Cross-protocol
   separation comes from protocol_id in the KDF inputs and from
   aad_label in the segment AAD, not from LH.

Sullivan                 Expires 14 January 2027               [Page 76]
Internet-Draft                    raAE                         July 2026

   A concrete SEAL suite specifies the following.

   AEAD:  One of the algorithms from Section 4.10.1, with associated Nk,
      Nn, and Nt values.

   KDF:  One of the KDF cipher suites from Table 11.

   nonce_mode:  A per-object payload_info field constrained by the
      profile, not a suite-fixed lock.  Table 10 gives the default mode
      each suite uses in the mutable profile.  A profile MAY select any
      valid (nonce_mode, snap_id) tuple (Section 6.5): in the mutable
      profile an MRAE AEAD MAY use either mode, and the immutable
      profile SEAL-RO-v1 pairs a derived nonce with any AEAD because
      write-once keeps every derived nonce unique.

   epoch_length:  The key-rotation granularity, with range given by
      Table 10.  Per Section 5, a 96-bit-nonce AEAD rotates the segment
      key to bound per-key nonce-collision risk and an MRAE AEAD rotates
      to bound its per-key encryption count, so both take epoch_length
      in 0 to 63.  A 256-bit-nonce AEAD makes both negligible and uses a
      flat key.

   segment_max:  One of the sizes from Section 4.10: 16384 or 65536
      octets.

   Fábrega et al.  ([FLRR25]) proved ra-ROR and ra-CMT security for a
   construction that instantiates the same component contracts
   enumerated above, and SEAL's analysis of those notions follows the
   same structure.  SEAL's additional components (the epoch keys and the
   snapshot maintained across rewrite and length change) are analyzed in
   Section 5.6 and Section 5.8.

5.6.  Segment Confidentiality and Integrity

   The construction targets ra-ROR security (Section 5.2.1).  The ra-ROR
   advantage bound, its adversary parameters, and the per-term analysis
   are in Appendix C, with the full proof in preparation ([SEALPROOFS]).
   This section states the property and the operational quantities a
   deployment needs.

   Ciphertext cores are indistinguishable from random under the ra-ROR
   definition, up to the public length leakage, the equality leakage
   allowed by derived nonce mode, and public metadata exposed by the
   consuming format (file-level headers, stored nonces in random mode,
   the salt, and the commitment).  Each segment AEAD ciphertext binds
   the segment to its own index and finality bit (through segment_aad in
   random nonce mode and through the nonce in derived nonce mode) so an
   adversary cannot modify or substitute a segment under a given index,

Sullivan                 Expires 14 January 2027               [Page 77]
Internet-Draft                    raAE                         July 2026

   or flip its finality bit, without causing an AEAD verification
   failure on that segment.  Same-index rollback to an older valid
   segment within the same message is not detected by per-segment AEAD
   alone.  This requires the snapshot verification of Section 4.7.

   The multi-message advantage grows linearly in the number of messages.
   In derived nonce mode the salt-collision term q_m^2/2^256 is the only
   quadratic floor for the 256-bit-key suites.  For AES-128-GCM the
   epoch-key collision floor E^2/2^128 (Appendix C.2) is an additional
   quadratic term that dominates.  In random nonce mode, with or without
   the plaintext-bound hedge, the nonce-collision term is also
   quadratic:

   nonce collision:  q^2 / 2^(8*Nn + 1)
   at Nn = 12:       q^2 / 2^97

   where q is the number of segment encryptions under one key and Nn is
   the nonce length in octets.  The block-size birthday term is derived
   in Section 5.9.

   Segment size enters the analysis only through the forgery and block-
   size terms, both bounded in Section 5.9.  It does not affect the
   confidentiality or commitment terms.  A segment's length is
   authenticated implicitly by its AEAD tag, not through segment_aad or
   the nonce, so the analysis assumes the consuming format conveys each
   segment's true ciphertext length.  Segments shorter than segment_max
   consume no more than their share of the per-key budget and need no
   separate accounting.

5.7.  Commitment Security

   The ra-CMT notion is defined in Section 5.2.2.

   raAE's commitment target is ra-CMT (random-access context
   commitment), not the per-AEAD notion CMT-1 ([RFC9771]).  CMT-1 is key
   commitment defined for AEADs, but raAE is a higher-level construction
   whose commitment binds the CEK and the full payload_info, which
   together carry the full parameter context that affects encryption,
   decryption, AAD construction, nonce construction, and key derivation.

Sullivan                 Expires 14 January 2027               [Page 78]
Internet-Draft                    raAE                         July 2026

   ra-CMT relies on the collision resistance of the commitment
   derivation map over the tuple (protocol_id, "commit", CEK,
   payload_info, G, commitment_length), with G empty by default.  PRF
   security alone does not suffice, because the commitment adversary may
   choose the CEK and the context values (Section 4.5).  SEAL sets
   commitment_length = Nh, so the commitment offers about 2^128
   collision-search work with HKDF-SHA-256 and about 2^256 with
   TurboSHAKE-256.  The reduction outline for this assumption and the
   collision quantities for a general commitment_length are in
   Appendix C.4.

   The commitment bounds the ra-CMT variant in which the positions match
   (Section 5.2.2).  It does not bind the segment position.  Position
   binding for the ra-CMT-p variant is inherited from the underlying
   AEAD's commitment level.  Because the SEAL AEADs are not natively
   key-committing, position confusion under an adversarially chosen key
   is out of scope, consistent with [FLRR25].  A malicious encryptor
   that controls the CEK can craft a single ciphertext segment that
   opens at two positions, and the per-segment AEAD tag does not prevent
   it.

   The per-segment associated data A_i is likewise outside the
   commitment: SEAL binds A_i through segment_aad and the AEAD tag for
   that segment.  Contexts that differ only in A_i are therefore not
   commitment collisions.  Their separation relies on AEAD commitment
   for the segment AAD.  A_i is also rewritable across rewrites and is
   not bound at the snapshot level.  An application that needs it bound
   there MUST bind it externally.  The exact accounting of this term in
   the ra-CMT reduction is deferred to Appendix C.

   A deployment that must defend against a malicious encryptor who
   controls the CEK can close the adversarial-key position gap by one of
   two changes.  These are non-normative.  SEAL adds none of them to the
   construction.

   *  A context-committing AEAD, so that each segment ciphertext commits
      to its full decryption context rather than relying on the external
      commitment over (CEK, payload_info, G).

   *  A per-segment collision-resistant position commitment, on the
      order of 16 to 64 octets per segment, bound alongside the segment
      so that no segment opens at two positions.

   Several AEADs including AES-GCM ([NIST-SP-800-38D]) and
   ChaCha20-Poly1305 ([RFC8439]) lack strong native key or context
   commitment.  CMT-4 ([RFC9771]) is full commitment to key, nonce,
   associated data, and plaintext.  None of the SEAL AEADs is relied
   upon to provide CMT-1 or CMT-4.  In particular, AES-256-GCM-SIV is

Sullivan                 Expires 14 January 2027               [Page 79]
Internet-Draft                    raAE                         July 2026

   not a key-committing AEAD, and key-commitment attacks against it are
   known (for example partitioning oracles).  raAE obtains ra-CMT
   context commitment from the external commitment over (CEK,
   payload_info, G) regardless of the AEAD's own committing or non-
   committing properties, provided that the commitment check is not
   bypassed and that payload_info contains the full parameter context
   that influences any later derivation or AEAD operation.

   With commitment_length = Nh and Nt = 16, ra-CMT is bounded by the
   commitment collision resistance, and ra-CMT-p adds the per-segment
   AEAD commitment term.  The ra-CMT collision bound is derived in
   Appendix C.4, and the ra-CMT-p bound is stated there as a sum.  The
   two key regimes differ.  Under honestly generated keys the idealized
   per-pair tag-forgery floor is 2^(-128) because Nt = 16, and concrete
   per-AEAD authenticity bounds degrade with segment length
   (Section 5.9).  Under an adversarially chosen CEK the position term
   is the AEAD's commitment level, which the SEAL AEADs are not relied
   upon to provide.

   The global associated data G (Section 4.6) enters the committed map
   as an additional input, empty by default, and the collision-
   resistance argument above applies unchanged with G in the committed
   context.  This matches the ra-CMT treatment of global associated data
   in [FLRR25].

   The ra-CMT and ra-CMT-p notions are defined in [FLRR25].  SEAL
   realizes ra-CMT through the external commitment over (CEK,
   payload_info, G), independent of the underlying AEAD's own commitment
   level.  Position binding for ra-CMT-p is inherited from the
   underlying AEAD's commitment level, as described above.  This
   document states these properties and their component bounds.  The
   exact accounting of the per-segment associated data and position
   terms in the ra-CMT-p reduction is the open item deferred to the
   proof in preparation ([SEALPROOFS]).

5.8.  Snapshot Authenticator Security

   Snapshot authentication is an extension this document adds to the
   [FLRR25] raAE framework.  The raAE primitive and its ra-ROR and ra-
   CMT notions are taken unchanged from [FLRR25], whose proofs apply to
   SEAL's realization of the base interface.  The snapshot authenticator
   adds two operations (RewriteSeg, SnapVerify) over an auxiliary
   snapshot value and one new security property, snapshot integrity
   (Section 5.2.3).  Integrating its reduction into a single combined
   proof with the ra-ROR framework is in preparation and will be
   published separately ([SEALPROOFS]).

Sullivan                 Expires 14 January 2027               [Page 80]
Internet-Draft                    raAE                         July 2026

   raAE's guarantees form a ladder.  The table names what establishes
   each and who provides it, so the keyed snapshot is not read as sender
   attribution.

   +=========================+================+=========+==============+
   | Guarantee               | Established    | Scope   | Provided by  |
   |                         | by             |         |              |
   +=========================+================+=========+==============+
   | Segment confidentiality | AEAD           | segment | this         |
   | and integrity, index    | decrypt,       |         | construction |
   | and finality bound      | per segment    |         |              |
   +-------------------------+----------------+---------+--------------+
   | Segment-set integrity:  | SnapVerify     | whole   | this         |
   | content, position,      | over the       | object  | construction |
   | count                   | snapshot       |         |              |
   |                         | value          |         |              |
   +-------------------------+----------------+---------+--------------+
   | Sender attribution,     | signature      | whole   | consuming    |
   | third-party             | over the       | object  | protocol,    |
   | verifiability           | snapshot       |         | not provided |
   |                         | context        |         |              |
   +-------------------------+----------------+---------+--------------+
   | Freshness, whole-object | external       | whole   | consuming    |
   | rollback protection     | version or     | object  | protocol,    |
   |                         | freshness      |         | not provided |
   |                         | state          |         |              |
   +-------------------------+----------------+---------+--------------+

                    Table 17: What each layer guarantees

   Snapshot integrity is keyed by snap_key and protects the writer's
   current segment set against a storage or write adversary that does
   not hold snap_key.  A verifier who holds snap_key, hence the CEK from
   which it is derived, detects any added, dropped, reordered, re-
   marked, or otherwise altered segment.  Forgery of a fresh snapshot
   value gives no protection among parties that share the CEK, because
   any CEK-holder can recompute snap_key, and the value is not a third-
   party-verifiable commitment to a segment set.  Whether a fixed,
   externally authenticated snapshot value binds segment content against
   key holders depends on the authenticator (Section 5.8.2).  An
   application that needs sender attribution or third-party
   verifiability MUST add a signing layer over the snapshot context, and
   Section 5.8.2 states which authenticator's value can carry that
   layer.

   Per-segment AEAD binds each segment's index and finality bit.  The
   snapshot authenticator adds authentication of the full segment set
   (its tags at snap_id 0x0001, leaf digests of the stored segments at

Sullivan                 Expires 14 January 2027               [Page 81]
Internet-Draft                    raAE                         July 2026

   0x0002 and 0x0003) and the count n_seg as a single unit, which
   detects same-index rollback, segment-set modification, and count
   changes.  The index-set check in SnapVerify (Section 4.7.1) is
   mandatory: an authenticator's verify MAY accept a malformed index
   multiset, so without the check a duplicated or dropped index could
   pass.

   A profile MUST keep each of an authenticator's KDF labels inside the
   injective encode frame (Section 4.3) so distinct roles cannot collide
   on the primitive input.

   Whole-object rollback to a previously valid snapshot is out of scope
   and is treated in Section 6.6.  snap_key is not exposed through any
   public API.  It is derived internally per message from the CEK and
   salt, so snap_key exposure is not a threat surface in the
   construction itself.

5.8.1.  Masked Multiset Hash

   SEAL's masked multiset hash (snap_id 0x0001) publishes snapshot =
   wrapped_acc || snapshot_tag.  The snapshot tag is a MAC over the
   count and accumulator under snap_key, and wrapped_acc is that
   accumulator hidden behind a deterministic, tag-derived one-time pad.
   The mask is what stops a write adversary from recombining the
   differences between published accumulators into a non-historical
   segment set.  Its three labels, contrib_label, snapshot_tag_label,
   and snapshot_mask_label, are distinct under the encode frame.

   Its forgery advantage Adv_acc is bounded by a fresh-input MAC term, a
   mix-and-match term, and a birthday term in the number of published
   snapshots that the deterministic masking introduces, on no assumption
   beyond the multi-user PRF the key schedule already uses.
   Appendix C.2 states the bound and Appendix C.5 derives it.

5.8.2.  Digest Transcript

   SEAL's digest transcript (snap_id 0x0002) publishes one keyed KDF
   evaluation over the commitment, the segment count, and the ordered
   leaf list (Section 4.7.5).  Its transcript label is distinct under
   the encode frame, and the injective framing binds each leaf's
   position and the count.

   Unforgeability without snap_key rests on the same multi-user PRF
   assumption as the rest of the key schedule.  The transcript is a
   single PRF evaluation over the framed input, so its term in
   Appendix C.2 is a fresh-input forgery term alone: there is no
   accumulator to recombine, no mask, and no birthday term in the number
   of published snapshots.

Sullivan                 Expires 14 January 2027               [Page 82]
Internet-Draft                    raAE                         July 2026

   The leaf leaf(i) = LH(ct_i) || tag(i) gives the transcript two
   bindings against a key holder.  The first is content binding.
   LH(ct_i) (Section 4.3) is the ciphertext digest, and a fixed
   transcript binds the ordered leaf list under the collision resistance
   of the KDF over framed inputs, so the ciphertext binding rests on the
   collision resistance of LH for any segment length.  Every input to LH
   is public, so this is a collision-resistance requirement, separate
   from the PRF assumption above, and it does not degrade when the
   adversary holds the CEK and every key derived from it.  The digest
   transcript is still a symmetric authenticator: any snap_key holder
   can compute a valid snapshot for an object it writes itself.  What a
   key holder cannot do is exhibit a second segment set that verifies
   under a fixed, already-published transcript value.

   The second binding is the presented nonce, through the tag.  A non-
   committing AEAD lets a key holder reinterpret a fixed ciphertext:
   holding ct_i unchanged and presenting a different stored nonce yields
   a different plaintext under the same key (Section 5.7), with a tag
   the key holder recomputes.  The ciphertext bytes do not change, so a
   leaf of LH(ct_i) alone would not detect the substitution.  The tag
   does change, because it is a function of the nonce, so a leaf that
   carries the tag binds the presented nonce and rejects the
   reinterpretation.  How firmly it binds depends on the AEAD's nonce
   and tag widths: for the narrow-nonce suites, whose nonce width Nn is
   at most the tag width Nt, the tag is effectively injective in the
   nonce and the binding is structural, while for a wide-nonce suite (Nn
   greater than Nt, AEGIS in SEAL) a nonce change can preserve the tag
   only by a 2^(8*Nt) search, so there the binding is 2^(8*Nt) rather
   than structural.  The write-once profiles that select this leaf use
   derived nonces (Section 4.10.2), which are not stored, so a wide
   nonce raises no reinterpretation concern in deployment.  The tag also
   binds the segment index and finality, through the segment AAD in
   random nonce mode and through the nonce in derived nonce mode
   (Section 4.4.2).

   This nonce binding is why the digest transcript places no restriction
   on the nonce mode.  In random nonce mode the nonce is stored and a
   key holder could otherwise move it, and the tag in the leaf closes
   that.  In derived nonce mode the nonce is recomputed from the key
   schedule and cannot be moved independently, so the tag is redundant
   there but not harmful.  A leaf without the tag, under a non-
   committing AEAD in random nonce mode, would be forgeable by a key
   holder, so SEAL's leaf carries the tag in every case.

   The masked multiset hash gives no such binding, in two independent
   ways.  Its contributions bind AEAD tags, and under an adversarially
   known CEK a non-committing AEAD admits a second ciphertext with the
   same tag (Section 5.7), so segment content can change under an

Sullivan                 Expires 14 January 2027               [Page 83]
Internet-Draft                    raAE                         July 2026

   unchanged accumulator.  Separately, its aggregation is linear: a
   party that knows snap_key can evaluate contributions offline and
   search for a different tag list with the same accumulator by a
   generalized birthday attack, at a cost far below the PRF bound for
   objects of more than a few segments.  Neither observation weakens the
   masked multiset hash in its own threat model, which excludes key
   holders.  Both matter only when a snapshot value is lifted into a
   stronger statement.

   That lifting is the signing layer this section requires for sender
   attribution.  A signature or MAC over a snapshot value carries
   content binding against CEK holders only when the underlying value
   already binds content against key holders.  The digest transcript is
   designed for that use, and its commitment element carries the full
   object context (CEK, payload_info, and G) into the authenticated
   value, so a signed value binds one object.  An application MUST NOT
   rely on a signature over the masked multiset hash's snapshot value
   for content attribution among CEK holders.  A leaf over the tag
   alone, as the masked multiset hash uses, would bind content only at
   the AEAD's commitment level.  The ciphertext digest is what grounds
   content binding on collision resistance.  A fixed, authenticated
   digest transcript also strengthens position binding.  It pins each
   segment's ciphertext to its index, so no one, not even a party that
   holds or chose the CEK, can take a valid segment and make it verify
   at a different index.  The base construction pins position only as
   strongly as the AEAD commits under an adversarial key, which the SEAL
   suites do not (Section 5.7), so a CEK holder can craft one ciphertext
   that opens at two positions.  The transcript closes that gap by
   grounding position on collision resistance instead.  In the terms of
   Section 5.7, this is the position-commitment notion ra-CMT-p,
   restored against adversarially chosen keys.  SEAL's leaf carries both
   the ciphertext digest and the tag, because no SEAL suite carries a
   commitment claim (Section 5.7) and all admit random nonce, so neither
   component is redundant.

   The formal forgery and content-binding reductions for the digest
   transcript are deferred to the combined proof in preparation
   ([SEALPROOFS]), with SEAL's other snapshot proofs.

5.8.3.  Epoch Digest Tree

   The epoch digest tree (snap_id 0x0003, Section 4.7.6) has the same
   per-segment leaf and the same key as the digest transcript, so its
   content, nonce, and position bindings are those of Section 5.8.2.

   The two-level fold rests on the same collision-resistance assumption.
   An epoch head binds its epoch's leaf run through LH, and the snapshot
   binds the epoch heads through LH, so a forged snapshot requires an LH

Sullivan                 Expires 14 January 2027               [Page 84]
Internet-Draft                    raAE                         July 2026

   collision at one of the two levels or a snap_key PRF forgery on the
   head.  The snapshot binds the epoch heads in order and binds n_seg,
   so an epoch head cannot move to another position and the segmentation
   is fixed by n_seg and epoch_length.

   The forgery term without snap_key is the digest transcript's single
   fresh-input PRF term, with no birthday term in the number of
   published snapshots.  Against a key holder, substituting content
   under a fixed published snapshot requires an LH collision against
   that fixed target, a second preimage, at one of three target classes:
   the n_seg leaves it shares with the digest transcript, the E epoch
   heads, and the single root.  The work factor is 2^(8*Nh) per target
   with a benign multiplicity of n_seg + E + 1, so the two fold levels
   add the epoch and root targets to the transcript's leaf targets
   without lowering the per-target strength.  A writer that chooses both
   openings of one snapshot instead faces a birthday collision at
   2^(4*Nh), the same equivocation floor as the digest transcript, which
   the fold does not lower.  The formal two-level reduction, the
   snap_key PRF term on the head and the LH terms at the epoch and root
   levels, is deferred to the combined proof in preparation
   ([SEALPROOFS]), with the digest transcript's.

5.9.  Capacity and Usage Limits

   These usage limits are organized around independent limit classes,
   not around named suites.  The accounting unit is one segment
   encryption under one segment-encryption key (an epoch key).  For
   random-nonce AEADs that budget is a single per-epoch-key pool.  For a
   derived-nonce MRAE AEAD (AES-256-GCM-SIV in SEAL) there are two
   separate budgets: a per-epoch-key budget for distinct derived nonces,
   and a per-derived-nonce budget for repeated encryption of one
   segment.  Such an AEAD therefore does not follow a simple "divide the
   per-key budget by 2^epoch_length" model.

   The length-dependent limits in this section are computed at the
   suite's segment_max, the largest plaintext one segment encryption
   carries.  Independent of these per-key budgets, a segment-encryption
   procedure MUST respect each underlying AEAD's per-invocation input
   limits.

5.9.1.  Accounting Model

   One segment encryption consumes one write from a segment-encryption
   key's budget.  Initial writes and rewrites are both segment
   encryptions.  The budget belongs to the epoch key, not the CEK.

   epoch-key budget:  the segment encryptions allowed under one epoch
      key.

Sullivan                 Expires 14 January 2027               [Page 85]
Internet-Draft                    raAE                         July 2026

   write (initial write):  the first encryption of a segment.

   rewrite:  a later encryption of the same segment.

   epoch_length (r):  the base-2 log of the number of segments that
      share one epoch key.

   For a random-nonce AEAD an epoch key has one budget, shared by the
   segments under it.  For a derived-nonce MRAE AEAD an epoch key has
   two: how many distinct segment nonces it may cover, and how many
   times any one segment may be re-encrypted at its fixed nonce.

5.9.2.  Limit Classes

   Six phenomena bound how much may be encrypted.  Each is set by one
   property of the AEAD or the segment size, and each bounds
   confidentiality or integrity.

   +================+===============+===============+=================+
   | Limit class    | Applies to    | Depends on    | Failure mode    |
   +================+===============+===============+=================+
   | Random-nonce   | random nonce  | nonce size    | confidentiality |
   | collision      | modes         |               |                 |
   +----------------+---------------+---------------+-----------------+
   | Forgery bound  | all AEADs     | MAC strength, | integrity       |
   |                |               | segment size  |                 |
   +----------------+---------------+---------------+-----------------+
   | Block-size     | block-cipher  | total blocks  | confidentiality |
   | birthday       | AEADs         |               |                 |
   +----------------+---------------+---------------+-----------------+
   | MRAE distinct- | derived-nonce | derived-nonce | confidentiality |
   | nonce          | MRAE across   | count         | and integrity   |
   |                | segments      |               |                 |
   +----------------+---------------+---------------+-----------------+
   | Fixed-nonce    | derived-nonce | segment size, | confidentiality |
   | data volume    | hot rewrites  | rewrites      |                 |
   +----------------+---------------+---------------+-----------------+
   | Epoch-key      | 128-bit-key   | key size      | confidentiality |
   | collision      | AEADs         |               |                 |
   +----------------+---------------+---------------+-----------------+

                      Table 18: Usage-limit classes

   Random-nonce collision and the block-size birthday bound the
   confidentiality of fresh-nonce encryption, and forgery bounds
   integrity for every decryption.  Two further classes apply only in
   derived-nonce MRAE mode: a derived-key collision across the distinct
   segment nonces under an epoch key, and a data-volume limit on

Sullivan                 Expires 14 January 2027               [Page 86]
Internet-Draft                    raAE                         July 2026

   repeated encryption of a single hot segment, one rewritten many times
   at its one derived nonce.  A sixth class applies only to 128-bit-key
   AEADs: a collision across the distinct epoch keys that holds the
   number of epoch keys per payload_key below about 2^48
   (Section 5.9.6).

5.9.3.  Binding Limits for Profiled Suites

   For each profiled suite the binding limit is the smallest applicable
   class.  At 65536-octet (64 KiB) segments and a 2^-32 advantage
   target:

Sullivan                 Expires 14 January 2027               [Page 87]
Internet-Draft                    raAE                         July 2026

   +====================+================+===========+=================+
   | Suite / mode       | Binding limit  | Budget    | Failure mode    |
   +====================+================+===========+=================+
   | AES-128-GCM,       | random-nonce   | ~2^32     | confidentiality |
   | random             | collision      | per       |                 |
   |                    |                | epoch     |                 |
   |                    |                | key       |                 |
   +--------------------+----------------+-----------+-----------------+
   | AES-256-GCM,       | random-nonce   | ~2^32     | confidentiality |
   | random             | collision      | per       |                 |
   |                    |                | epoch     |                 |
   |                    |                | key       |                 |
   +--------------------+----------------+-----------+-----------------+
   | ChaCha20-Poly1305, | random-nonce   | ~2^32     | confidentiality |
   | random             | collision      | per       |                 |
   |                    |                | epoch     |                 |
   |                    |                | key       |                 |
   +--------------------+----------------+-----------+-----------------+
   | AES-256-GCM-SIV,   | MRAE distinct- | ~2^48     | confidentiality |
   | derived, distinct  | nonce          | per       | and integrity   |
   | segments           |                | epoch     |                 |
   |                    |                | key       |                 |
   +--------------------+----------------+-----------+-----------------+
   | AES-256-GCM-SIV,   | fixed-nonce    | ~2^36     | confidentiality |
   | derived, hot       | data volume    | per       |                 |
   | segment            |                | segment   |                 |
   |                    |                | at 64     |                 |
   |                    |                | KiB       |                 |
   +--------------------+----------------+-----------+-----------------+
   | AEGIS-256, random  | forgery bound  | ~2^83     | integrity       |
   |                    |                | per key   |                 |
   +--------------------+----------------+-----------+-----------------+
   | AEGIS-256X2,       | forgery bound  | ~2^83     | integrity       |
   | random             |                | per key   |                 |
   +--------------------+----------------+-----------+-----------------+

           Table 19: Binding limits for profiled suites at 64 KiB

   The 96-bit-nonce suites bind on random-nonce collision.  AEGIS makes
   nonce collision negligible with a 256-bit nonce (~2^112) and binds on
   forgery.  A derived-nonce MRAE AEAD has the two limits above, the
   distinct-derived-nonce ceiling and the fixed-nonce data-volume cap,
   not a single shared pool.

5.9.4.  Maximum Write-Once Object Size

   Two limits bound a write-once object: each epoch key's AEAD write
   budget, and the 2^63 segment index ceiling.

Sullivan                 Expires 14 January 2027               [Page 88]
Internet-Draft                    raAE                         July 2026

   Under a flat key (one epoch key for the whole object), the AEAD
   budget sets the size:

         +==========================+===========================+
         | Suite                    | Flat-key write-once limit |
         +==========================+===========================+
         | AES-128-GCM, AES-256-GCM | ~2^36 segments            |
         +--------------------------+---------------------------+
         | AES-256-GCM-SIV          | ~2^48 segments            |
         +--------------------------+---------------------------+
         | ChaCha20-Poly1305,       | 2^63 segments             |
         | AEGIS-256, AEGIS-256X2   |                           |
         +--------------------------+---------------------------+

                 Table 20: Flat-key write-once size limit

   For the block ciphers this limit is a confidentiality bound.  The
   AES-GCM suites (AES-128-GCM and AES-256-GCM) bind on the block-size
   birthday and AES-256-GCM-SIV on the distinct-derived-nonce budget.
   ChaCha20-Poly1305 and AEGIS have no write-count confidentiality bound
   below the index ceiling.  The AES-128-GCM, AES-256-GCM, and
   ChaCha20-Poly1305 figures assume derived nonce mode, which these non-
   MRAE suites may use only in a write-once profile.  In the default
   random nonce mode they bind earlier, at the random-nonce-collision
   budget of about 2^32 per epoch key (Table 19).

   With epoch rotation each epoch key carries a fresh budget, so any
   suite can reach the 2^63-segment ceiling, about 2^79 octets at 64 KiB
   segments.  For AES-128-GCM this requires a sufficiently large
   epoch_length.  At epoch_length 0 its epoch-key collision floor caps
   the object near 2^48 segments (Section 5.9.6).  Reaching 2^63 needs
   epoch_length at least 15, so the distinct epoch keys stay near 2^48.
   A rotating profile MUST keep each epoch key's 2^epoch_length initial
   writes within its per-key budget.  For a derived-nonce MRAE AEAD that
   means 2^epoch_length below the distinct-derived-nonce budget of about
   2^48.  Rewrite capacity is a separate condition (Section 5.9.5).

5.9.5.  Rewrite Capacity

   A rewrite consumes the same budget as an initial write.  How that
   budget is shared depends on the nonce mode.

   For the random-nonce AEADs, epoch_length divides one per-epoch-key
   pool among the 2^epoch_length segments that share the key.  If
   rewrites are spread evenly, the count each segment can take falls as
   epoch_length rises:

Sullivan                 Expires 14 January 2027               [Page 89]
Internet-Draft                    raAE                         July 2026

    +====================+==============+==============+=============+
    | Suite / mode       | epoch_length | Segments per | Rewrites    |
    |                    |              | epoch key    | per segment |
    +====================+==============+==============+=============+
    | AES-128-GCM, AES-  | 0            | 1            | ~2^32       |
    | 256-GCM, random    |              |              |             |
    +--------------------+--------------+--------------+-------------+
    | AES-128-GCM, AES-  | 6            | 64           | ~2^26       |
    | 256-GCM, random    |              |              |             |
    +--------------------+--------------+--------------+-------------+
    | AES-128-GCM, AES-  | 10           | 1024         | ~2^22       |
    | 256-GCM, random    |              |              |             |
    +--------------------+--------------+--------------+-------------+
    | ChaCha20-Poly1305, | 0            | 1            | ~2^32       |
    | random             |              |              |             |
    +--------------------+--------------+--------------+-------------+
    | ChaCha20-Poly1305, | 6            | 64           | ~2^26       |
    | random             |              |              |             |
    +--------------------+--------------+--------------+-------------+
    | ChaCha20-Poly1305, | 10           | 1024         | ~2^22       |
    | random             |              |              |             |
    +--------------------+--------------+--------------+-------------+

                 Table 21: Rewrite share by epoch_length

   For a derived-nonce MRAE AEAD the hot-segment rewrite cap is not
   divided by 2^epoch_length, because a rewrite reuses the segment's one
   derived nonce:

     +==================+==============+==============+=============+
     | Suite / mode     | epoch_length | Segments per | Hot-segment |
     |                  |              | epoch key    | rewrite cap |
     +==================+==============+==============+=============+
     | AES-256-GCM-SIV, | 0            | 1            | ~2^36 at 64 |
     | derived          |              |              | KiB         |
     +------------------+--------------+--------------+-------------+
     | AES-256-GCM-SIV, | 6            | 64           | ~2^36 at 64 |
     | derived          |              |              | KiB         |
     +------------------+--------------+--------------+-------------+
     | AES-256-GCM-SIV, | 10           | 1024         | ~2^36 at 64 |
     | derived          |              |              | KiB         |
     +------------------+--------------+--------------+-------------+

              Table 22: GCM-SIV rewrite cap by epoch_length

   The hot-segment cap is length-dependent: about 2^36 at 64 KiB
   segments and about 2^38 at 16 KiB.  The distinct-derived-nonce budget
   stays about 2^48 per epoch key.  A derived-nonce MRAE AEAD thus has

Sullivan                 Expires 14 January 2027               [Page 90]
Internet-Draft                    raAE                         July 2026

   two separate constraints, the distinct derived nonces per epoch key
   and the repeated encryptions at one derived nonce.  AEGIS uses a flat
   key, and its forgery budget, about 2^83 per key from
   [I-D.irtf-cfrg-aegis-aead], exceeds any reachable rewrite workload.

   Applications MUST track segment encryptions per key and freeze the
   object before a budget is exceeded.  The CEK is fixed per object and
   cannot be rotated in place, so continued writing requires a new
   object under a fresh CEK.

5.9.6.  Choosing epoch_length

   epoch_length means different things by mode.  For the random-nonce
   AEADs, a smaller epoch_length puts fewer segments under each key, so
   each segment keeps a larger share of the random-nonce collision pool,
   at the cost of more epoch keys to derive and hold.  For a derived-
   nonce MRAE AEAD, a smaller epoch_length reduces the distinct derived
   nonces under an epoch key but does not raise the hot-segment rewrite
   cap, which is per derived nonce and length-dependent.  AEGIS has a
   256-bit nonce and a high forgery floor, so a flat key (epoch_length
   63) is the natural choice.  When the epoch digest tree (snap_id
   0x0003, Section 4.7.6) is selected, epoch_length also sets the
   transcript's grouping fan-out, and Table 7 gives the value that keeps
   random-access verification to one aligned read per epoch.

   The random-access benefit is realized only when epoch_length is in
   the grouping range of Table 7.  A profile that sets a large
   epoch_length for key rotation, such as an AEGIS suite's flat key at
   63, makes E = 1, so the fold is a single epoch: still correct, with
   no random-access benefit.  A single epoch is still the two-level
   epoch digest tree under its own labels, not the snap_id 0x0002 digest
   transcript value.  A profile that wants random-access verification
   sets epoch_length to the Table 7 value and accounts for the resulting
   per-epoch-key write budget in Section 5.9.

   The cipher suite's default epoch_length, used when a referencing
   protocol does not choose one, is 0 for the suites with an
   epoch_length range and 63 for the AEGIS suites.  epoch_length 0
   derives a fresh epoch key per segment, the finest rotation, giving
   each segment the largest share of the random-nonce collision pool.
   The AEGIS 256-bit nonce makes a flat key safe.  The named
   instantiations (Section 4.12) do not take this default: each pins an
   epoch_length tuned to its profile and object size, trading some
   rotation for fewer epoch keys while staying within budget.

Sullivan                 Expires 14 January 2027               [Page 91]
Internet-Draft                    raAE                         July 2026

   The epoch keys are independent under the PRF security of the KDF
   ([RFC8645]), so no epoch key is weaker than another.  The
   epoch_length parameter controls budget distribution, not key
   strength.

   For AES-128-GCM the 128-bit AEAD key adds one further constraint.
   The epoch-key collision term (Appendix C.2) is about E^2/2^128 over
   the E distinct epoch keys, so an AES-128-GCM profile SHOULD keep
   distinct epoch keys per payload_key below about 2^48, which holds
   that term within the 2^-32 target.  At epoch_length 0 this caps an
   AES-128-GCM object at about 2^48 segments, far below the 2^63 index
   ceiling, so it constrains only extreme object sizes.

5.9.7.  Derivations

   The per-suite figures come from the bounds below.

5.9.7.1.  Confidentiality (Nonce Collision)

   For random nonce mode with Nn-octet nonces and q segment encryptions
   under one key, the collision probability follows the birthday bound:

   P(collision) <= q^2 / 2^(8*Nn + 1)

   When epoch_length = r is specified, q counts encryptions per epoch
   key (initial writes plus rewrites within that epoch), not across the
   whole content.  Each epoch key has an independent budget.

   For derived nonce mode, nonces are deterministic and distinct across
   segment indices, so this collision term does not apply.  The limits
   on reusing a derived nonce are in Section 5.9.7.4.

5.9.7.2.  Confidentiality (Block-Size Birthday Bound)

   Nonce collisions alone do not exhaust the confidentiality bound.
   Following the AEAD usage-limits analysis ([I-D.irtf-cfrg-aead-limits]
   Section 5), block-cipher AEADs have a distinguishing bound that grows
   with the total number of cipher blocks processed under a key.  For
   the AES-GCM suites (AES-128-GCM and AES-256-GCM), if s is the total
   number of AAD-plus-plaintext 128-bit blocks and q is the number of
   encryption queries under a key, the confidentiality advantage is at
   most:

   CA <= ((s + q + 1)^2) / 2^129

   For 65536-octet segments (L = 4096 blocks per segment), s = L * q in
   the worst case, so CA scales as q^2 * L^2 / 2^129.  In random nonce
   mode the nonce-collision bound q^2 / 2^97 dominates for typical

Sullivan                 Expires 14 January 2027               [Page 92]
Internet-Draft                    raAE                         July 2026

   deployments because L^2 / 2^32 is small.  In derived nonce mode the
   nonce-collision term vanishes and the block-size term is the binding
   confidentiality constraint.

   For ChaCha20-Poly1305 there is no comparable block-size bound because
   ChaCha20 is a stream cipher, leaving nonce collision and forgery as
   the only relevant terms.  Section 5.9.7.4 gives the derived-nonce
   block-size limits.  No analogous block-size confidentiality bound has
   been published for AEGIS-256 or AEGIS-256X2.  Nonce collision
   (negligible at a 256-bit nonce) and the per-key margin govern AEGIS
   confidentiality.  The per-query forgery bound governs integrity.

   SEAL implementations MUST compute usage budgets from the AEAD-
   specific confidentiality and integrity bounds, not from nonce-
   collision probability alone.  Table 19 gives the binding limit per
   suite.

5.9.7.3.  Integrity (Forgery)

   Each AEAD decryption query gives the adversary a chance to forge a
   valid ciphertext.  The forgery advantage per query depends on the
   AEAD and segment_max.

   For the AES-GCM suites (AES-128-GCM and AES-256-GCM)
   ([I-D.irtf-cfrg-aead-limits] Section 5.1):

   IA <= 2 * v * (L + 1) / 2^128

   where v is the number of forgery attempts and L = 4096 blocks per
   segment.

   For ChaCha20-Poly1305 ([I-D.irtf-cfrg-aead-limits] Section 5.2):

   IA <= v * (L' + 1) / 2^103

   Here v is the number of forgery attempts and L' is the segment length
   in 16-octet Poly1305 blocks (about 4096 at 65536-octet segments), per
   [I-D.irtf-cfrg-aead-limits].  The 2^103 denominator (not 2^128)
   reflects Poly1305's per-query forgery bound.  At 65536-octet
   segments, the integrity limit for ChaCha20-Poly1305 is tighter than
   for AES-GCM.

5.9.7.4.  Derived-Nonce Bounds

   A derived-nonce MRAE AEAD has two limits the random-nonce analysis
   above does not capture.

Sullivan                 Expires 14 January 2027               [Page 93]
Internet-Draft                    raAE                         July 2026

   Across the distinct segment nonces under one epoch key, the derived-
   key analysis of [RFC8452] Section 9 ([BHT18]) bounds the number of
   distinct derived nonces at about 2^48 for a 2^-32 advantage.

   For a single hot segment, every rewrite reuses that segment's one
   derived nonce, so N rewrites of an L-block segment run AES-CTR under
   one derived per-record key.  The binding term is the keystream block-
   size birthday over the total blocks, in the form of
   [I-D.irtf-cfrg-aead-limits] Section 5 with s = N * L blocks and q = N
   queries:

   ((s + q + 1)^2) / 2^129 <= 2^-32

   The N * L term dominates, so this holds while N * L stays below about
   2^48, giving N <= about 2^48 / L.  That is about 2^36 rewrites of one
   64 KiB segment (L = 4096) and about 2^38 at 16 KiB.  This per-nonce
   cap does not divide by 2^epoch_length, because the segment reuses the
   same derived nonce regardless of epoch_length.

   Distinct plaintexts under the fixed nonce produce distinct synthetic
   IVs and hence distinct counter keystreams (the GCM-SIV synthetic-IV
   construction, [RFC8452]), so confidentiality degrades only by this
   block-birthday term and not by nonce reuse.  Two rewrites with
   identical plaintext and associated data produce identical ciphertext
   (deterministic-MRAE equality leakage), independent of this count.

6.  Security Considerations

   raAE's ra-ROR security target (Section 5.2.1) rests on three
   assumptions: the AEAD is multi-user real-or-random (mu-ROR) secure,
   the KDF is a secure multi-user pseudorandom function (mu-PRF), and
   nonces do not collide.  Each subsection below describes a way one of
   these can fail and what breaks.

   Integrity across the SEAL suite is bounded by the 16-octet AEAD tag
   (Nt = 16): no suite member offers more than approximately 128 bits of
   forgery resistance per query (Table 19).  The AEGIS algorithms'
   256-bit keys raise confidentiality margins, not the tag-length
   forgery floor.

6.1.  Detection Summary

   The table below maps common failure modes to the check that detects
   them and notes the cases where raAE provides no detection.  The rows
   detected by SnapVerify assume a snapshot authenticator is configured
   (snap_id != 0x0000).  Without one, those modes go undetected.  The
   one exception is a dropped trailing segment: the per-segment finality
   requirement of Section 4.9.1.2 detects it in any profile, with no

Sullivan                 Expires 14 January 2027               [Page 94]
Internet-Draft                    raAE                         July 2026

   snapshot authenticator.

    +==============+==================================================+
    | Failure mode | Detected by                                      |
    +==============+==================================================+
    | Wrong CEK,   | The commitment, before any segment decryption,   |
    | salt, or     | provided reader and writer use the same          |
    | parameter    | payload_info construction.                       |
    | set          |                                                  |
    +--------------+--------------------------------------------------+
    | Modified     | AEAD.Decrypt for the affected segment.           |
    | ciphertext   |                                                  |
    | core or AEAD |                                                  |
    | tag          |                                                  |
    +--------------+--------------------------------------------------+
    | Segment      | AEAD.Decrypt: the segment index and finality bit |
    | moved to a   | are authenticated by segment_aad (random nonce   |
    | different    | mode) or by the nonce (derived nonce mode).      |
    | index        |                                                  |
    +--------------+--------------------------------------------------+
    | Segment      | Detected when the copied segment was encrypted   |
    | copied from  | under a different CEK or salt.  Reusing a salt   |
    | another      | with the same CEK breaks this separation         |
    | object at    | (Section 6.7).                                   |
    | the same     |                                                  |
    | index        |                                                  |
    +--------------+--------------------------------------------------+
    | Segment      | Not detected by per-segment AEAD alone; detected |
    | copied from  | by SnapVerify, unless the whole object           |
    | another      | (including snapshot) is rolled back to that      |
    | version at   | earlier valid version.                           |
    | the same     |                                                  |
    | index        |                                                  |
    +--------------+--------------------------------------------------+
    | Dropped      | The finality requirement of Section 4.9.1.2, in  |
    | trailing     | any profile: the highest-indexed present segment |
    | segment(s)   | then carries is_final = 0 and the decryptor      |
    | (truncation) | rejects.  Truncation to zero present segments is |
    |              | rejected by the zero-segment prohibition without |
    |              | a snapshot authenticator, and detected by        |
    |              | SnapVerify with one (Section 6.6).               |
    +--------------+--------------------------------------------------+
    | Missing      | SnapVerify, which checks the segment count,      |
    | interior,    | finality, and recomputed snapshot value.         |
    | duplicated,  |                                                  |
    | reordered,   |                                                  |
    | or inserted  |                                                  |
    | segment      |                                                  |

Sullivan                 Expires 14 January 2027               [Page 95]
Internet-Draft                    raAE                         July 2026

    +--------------+--------------------------------------------------+
    | Stale        | SnapVerify, unless the stale value is part of a  |
    | snapshot     | whole-object rollback to a previously valid      |
    | value        | snapshot.                                        |
    +--------------+--------------------------------------------------+
    | Whole-object | Not detected by raAE alone; the consuming        |
    | rollback     | protocol needs authenticated external freshness  |
    |              | state.                                           |
    +--------------+--------------------------------------------------+
    | Concurrent   | Not a cryptographic forgery; the consuming       |
    | lost update  | protocol needs writer serialization or another   |
    |              | way to publish object state atomically.          |
    +--------------+--------------------------------------------------+
    | Equality     | Not an authentication failure; rewriting equal   |
    | leakage in   | plaintext at the same index under derived nonce  |
    | derived      | mode can reveal equality (Section 4.5.3,         |
    | nonce mode   | Section 6.2).                                    |
    +--------------+--------------------------------------------------+

          Table 23: Failure modes and the checks that detect them

6.2.  Nonce Misuse

   Nonce reuse under a non-MRAE AEAD leaks plaintext: an adversary who
   observes two ciphertexts under the same key and nonce recovers the
   XOR of the plaintexts (for CTR-based AEADs) and can forge new
   ciphertexts.  For AES-GCM and ChaCha20-Poly1305, nonce reuse also
   recovers the polynomial authentication key, enabling forgery of
   arbitrary messages.

   Random nonce mode depends entirely on the CSPRNG.  If the CSPRNG
   returns duplicated state, segments collide.  Derived nonce mode
   removes that dependence, since nonces are deterministic, but the
   determinism is itself a hazard: re-encrypting a segment with
   different content under its repeated derived nonce is a two-time pad,
   catastrophic for a non-MRAE AEAD.

   A non-MRAE AEAD therefore uses derived nonces only in a write-once
   profile that draws a fresh salt per object and never re-encrypts
   under it after a crash (Section 4.5.3.2).  An MRAE AEAD instead
   degrades only to equality leakage, not plaintext recovery.

   The plaintext-bound construction (Appendix A) partially defends
   against CSPRNG duplication: different plaintexts at the same index
   produce different nonces because the plaintext digest differs, but
   equal plaintexts still collide.  Implementations that need a full
   defense against random number generator (RNG) state duplication MUST
   use derived nonce mode with an MRAE AEAD.

Sullivan                 Expires 14 January 2027               [Page 96]
Internet-Draft                    raAE                         July 2026

6.3.  Parameter Set Mismatch

   The full parameter set that affects encryption, decryption, AAD
   construction, nonce construction, and key derivation is bound into
   payload_info (see Section 4.2) and therefore into the commitment.  A
   reader using a different parameter set than the writer triggers a
   commitment mismatch before any AEAD operation is attempted.  A reader
   supplying a different G than the encryptor likewise triggers a
   commitment mismatch (Section 4.6).  Consuming protocols MUST still
   reject unrecognized or unsupported parameter values before
   decryption, since the commitment check detects mismatch but does not
   by itself indicate which parameter value the recipient is unable to
   support.

   One profile-level constant is bound transitively rather than via
   payload_info: aad_label is bound through protocol_id (each profile
   fixes its own aad_label).  nonce_mode, by contrast, is carried in
   payload_info, so the commitment binds it directly.  Each SEAL AEAD
   additionally sets a default nonce mode (see Table 10).  Profiles MUST
   NOT share a protocol_id across distinct aad_label values.  Reusing a
   protocol_id with a changed aad_label produces objects whose
   commitment matches the wrong reader but whose per-segment AEAD
   verification fails with no clear error attribution.

6.4.  Framing and Label Errors

   Two classes of implementation error break cross-role isolation.  A
   non-injective framing function maps distinct KDF input tuples to the
   same primitive input, correlating outputs that should be independent.
   Implementations MUST verify injectivity per Section 5.5.  Reusing a
   label across roles (for example, "commit" for both commitment and
   payload key) has the same effect.  Labels MUST be distinct within a
   protocol version, and a new version that changes any derivation MUST
   change the protocol_id.

6.5.  Parameter Misuse

   The nonce mode and AEAD choice are coupled, and the table gives the
   rule for each pairing:

Sullivan                 Expires 14 January 2027               [Page 97]
Internet-Draft                    raAE                         July 2026

    +============+==============+====================================+
    | AEAD class | random nonce | derived nonce                      |
    +============+==============+====================================+
    | non-MRAE   | valid        | mutable: unsafe; write-once: valid |
    +------------+--------------+------------------------------------+
    | MRAE       | wasteful     | valid                              |
    +------------+--------------+------------------------------------+

                    Table 24: Nonce mode by AEAD class

   Rewrite is where the coupling matters.  A derived nonce is fixed by
   the segment index, so a rewrite reuses it, while a random nonce is
   drawn fresh every time.  A repeat is catastrophic for a non-MRAE AEAD
   and only equality-leaking for an MRAE one, which is what the table
   reflects.  Section 4.5.3.2 gives the rules and their consequences.

   The optional plaintext-bound construction (Appendix A) is an
   encryptor-side hedge over random nonce mode, not a separate nonce
   mode.  See Section 4.10.1 for SEAL's per-AEAD guidance.

6.6.  Snapshot Integrity Limitations

   Snapshot integrity has the following limitations.

   First, a reader that does not run snapshot verification (Section 4.7)
   does not verify that a segment belongs to the current authenticated
   snapshot.  Such a reader gets per-segment AEAD authenticity only,
   without the snapshot integrity guarantee of Section 5.2.3: the
   ciphertext core is authenticated under the segment index and finality
   bit, but the reader cannot detect that the segment was substituted
   from a previous valid version of the same message at the same index,
   nor that other segments were dropped or rolled back.  Such readers
   MUST still verify the commitment per Section 4.6.  Applications that
   support random-access single-segment reads MUST either run snapshot
   verification on every read or explicitly document that they accept
   per-segment authenticity without snapshot freshness.

   Conversely, snapshot verification authenticates the segment set, its
   positions, and the count.  Over the segment tags (snap_id 0x0001) it
   does not confirm that a tag is a valid AEAD tag for the ciphertext
   beside it.  Over leaf digests (snap_id 0x0002 and 0x0003) it binds
   the ciphertext bytes and the tag, but not that the tag opens the
   ciphertext under the AEAD.  Decrypting the segment is what
   establishes that, so snapshot verification layers set, position, and
   count binding on top of per-segment AEAD without replacing it.

Sullivan                 Expires 14 January 2027               [Page 98]
Internet-Draft                    raAE                         July 2026

   Second, snapshot integrity does not provide freshness against whole-
   object rollback.  A storage adversary that rolls back the entire
   encrypted object, including the snapshot, to a previously valid
   version is not detected by raAE alone, because the rolled-back
   snapshot is itself a valid snapshot for that prior state.  This is
   replay of an intact prior snapshot, not a forgery: it stays out of
   scope unless the consuming protocol authenticates external freshness
   into the snapshot context.  Applications that require whole-object
   rollback resistance MUST bind an authenticated version field,
   timestamp, monotonic counter, or authenticated storage layer into
   that context.  Relatedly, Section 4.9.2.1 requires a writer to update
   only trusted snapshot state, so a rewrite cannot launder a rolled-
   back snapshot into a valid ongoing history.

   Third, same-index rollback within a snapshot is detected when
   snapshot verification runs.  If an adversary replaces one segment
   with a previously valid same-index segment but leaves the current
   snapshot value in place, the recomputed snapshot value no longer
   matches the stored one, so verification fails except with negligible
   forgery probability (see Section 5.8).

   Detecting these substitutions is a benefit of the design, not a
   limitation.  It is the property that distinguishes raAE's snapshot
   integrity from per-segment AEAD authenticity.

   Truncation, including removal of the final segment marked is_final =
   1, is detected by snapshot verification.  The removed segment is
   absent from the recomputed snapshot value and the count n_seg no
   longer matches the segments present, so verification fails.
   Truncation is not a separate limitation.  It is a special case of the
   first limitation above for readers that skip snapshot verification.

6.7.  Salt Reuse

   Reusing a salt with the same CEK across two files produces identical
   payload schedule outputs: the same payload_key, the same snap_key,
   and, in derived nonce mode, the same nonce_base.  The damage depends
   on the nonce mode.

   In every nonce mode, an adversary can silently swap same-index
   segments between the two files: both per-segment AEAD checks and the
   snapshot authenticator accept the swap.  Salt reuse is therefore an
   integrity break in every configuration.

Sullivan                 Expires 14 January 2027               [Page 99]
Internet-Draft                    raAE                         July 2026

   In derived nonce mode the nonces also repeat across the two files, so
   an MRAE AEAD degrades to deterministic encryption and leaks plaintext
   equality between same-index segments.  A non-MRAE AEAD under derived
   nonces, a combination the construction forbids (Section 4.5.3.2),
   would permit plaintext recovery.

   In random nonce mode (and with the plaintext-bound hedge) fresh
   nonces keep confidentiality intact, although the per-key nonce-
   collision budget of Section 5.9.7.1 then counts encryptions across
   both files under the single CEK.  The integrity break above remains.
   Applications MUST therefore ensure salt uniqueness per CEK when
   creating a new message.  The freshness requirement in Section 4.9.1.1
   exists for this reason.

6.8.  Rewrite Hazards

   Rewriting introduces the following hazards.

   Applications MUST track total segment encryptions per key and freeze
   the object before exceeding the budget in Section 5.9.5.  For the 96-
   bit-nonce AEADs (AES-128-GCM, AES-256-GCM, ChaCha20-Poly1305) the
   binding limit is the per-epoch-key collision pool of roughly 2^32
   encryptions, counted across every segment that shares the key.
   Spread evenly, that pool gives each segment a uniform rewrite share
   of roughly 2^(32 - epoch_length), as tabulated in Section 5.9.5.  See
   Section 5.9.6.  Exceeding the per-epoch-key pool risks nonce
   collisions and plaintext recovery.

   A mutable profile with derived nonces and an MRAE AEAD (SEAL-RW-v1)
   reuses a segment's deterministic nonce across non-terminal rewrites,
   and a crash that replays a write reuses it again.  Confidentiality
   there rests on the per-segment rewrite limit (Section 5.9.5), not on
   nonce uniqueness: the AEAD degrades to equality leakage between
   identical rewrites rather than to plaintext recovery, as long as that
   budget is respected.

   For the masked multiset hash, when multiple writers rewrite different
   segments concurrently, each computes an independent accumulator
   difference (old_contrib XOR new_contrib).  Applying these differences
   is commutative, but the read-modify-write on the stored accumulator
   requires coordination.  Supplying that coordination, for example a
   compare-and-swap on the published snapshot value and segment count,
   is the consuming protocol's responsibility, consistent with the
   serialization and storage transactions this document places out of
   scope.  The digest transcript (0x0002) and epoch digest tree (0x0003)
   have no accumulator to fold differences into, so concurrent rewrites
   under a mutable profile MUST serialize the snapshot recomputation and
   publication.  For 0x0003 the epoch head recomputation and write for

Sullivan                 Expires 14 January 2027              [Page 100]
Internet-Draft                    raAE                         July 2026

   each affected epoch MUST likewise be serialized so that the head
   binds the epoch's leaf run as read.  The snapshot tag and the mask
   MUST be recomputed over the final accumulator and segment count
   inside the same critical section that publishes the snapshot update.
   Otherwise a storage layer can publish a masked accumulator and a
   snapshot tag that were computed for different accumulators.  As a
   recovery mechanism, the accumulator can always be rebuilt from
   scratch by XOR-ing all contrib values and then re-masked.

   Length changes are a separate concurrency case.  Append and truncate
   change the segment count and re-mark a terminal segment.  Those
   updates do not commute with each other or with a concurrent rewrite
   of the old or new terminal segment unless the consuming protocol
   serializes them.

   Per-segment associated data A_i is rewritable, not fixed at creation.
   Unlike a global associated data value G, which the commitment fixes
   when the object is created (Section 4.6), A_i rides the per-segment
   AEAD associated data (Section 4.4.2), so a rewrite can replace a
   segment's A_i, for example to change a policy or version field.  A
   reader therefore cannot treat A_i as context fixed at creation the
   way it can treat G.  An application that needs context fixed at
   creation places it in G, which the commitment binds, rather than in
   A_i.

   raAE also does not guarantee atomic rewrites.  A segment rewrite
   touches the nonce metadata, ciphertext core, AEAD tag, and the
   snapshot value.  A crash between any two of these leaves the content
   inconsistent.  Applications MUST use write-ahead logging, copy-on-
   write, or an equivalent mechanism to make rewrites recoverable.

6.9.  Constant-Time Implementation

   Several raAE operations handle secret data and MUST be implemented in
   constant time to prevent timing side-channels.

   The KDF calls in the payload schedule and epoch key derivation take
   the CEK or payload_key as input keying material.  Implementations
   MUST ensure that HKDF-Extract and HKDF-Expand execute in constant
   time with respect to their key inputs.  In practice this is satisfied
   by HMAC implementations that do not branch on key octets.

   Both the commitment comparison (Section 4.9.1.2) and the snapshot
   comparison in SnapVerify MUST use a constant-time octet comparison.
   SnapVerify MUST compare the full recomputed snapshot value, the
   masked accumulator and the snapshot tag together, in one constant-
   time comparison, so that no observable difference reveals which half
   differs (Section 4.7.4).  A variable-time comparison reveals the

Sullivan                 Expires 14 January 2027              [Page 101]
Internet-Draft                    raAE                         July 2026

   position of the first differing octet.  An adversary who can retry
   tampered inputs learns the expected value one octet at a time and can
   then present a matching stored value.  This reduces the forgery cost
   of either check to a linear number of trials (for the snapshot, the
   bound of Section 5.8).

   The epoch digest tree's verify_segment (Section 4.7.6) compares
   recomputed values against stored ones at three levels, the snapshot,
   the epoch head, and the leaf, and MUST use a constant-time octet
   comparison at each.  The stored value compared against at each level
   is public metadata, and snap_key enters only through the KDF, whose
   constant-time requirement is stated above, so no comparison carries a
   secret.  Because the reference operands are public, the fused single-
   comparison rule for the masked multiset hash does not apply: a per-
   level early return reveals only which stored region a tampering party
   altered, which that party already knows, and leaks no keyed value.

   AEAD.Encrypt and AEAD.Decrypt operations inherit the constant-time
   requirements of the underlying AEAD.  Implementations SHOULD use AEAD
   libraries that document constant-time guarantees.

6.10.  Properties Not Provided

   raAE protects segment content and binds segments together.  It
   deliberately does not address four concerns that belong to the
   consuming protocol.

   The CEK must remain available as long as any reader needs access, so
   there is no forward secrecy.  Ciphertexts are not bound to any sender
   identity.  A signing or MAC layer is needed for sender
   authentication.  Key identifiers and structural features of the
   encrypted format are visible, so unlinkability requires application-
   layer measures.  Finally, snapshot verification covers segments
   within a message but cannot detect replacement of the message itself.
   An adversary who swaps one encrypted message for another goes
   undetected unless the application binds message identity externally.

   These concerns are not artifacts of raAE's design.  Any raAE
   construction inherits them from the consuming protocol's scope.

6.11.  Cross-Application Domain Separation

   Two applications that both reuse the same SEAL profile label
   (Section 4.10.2) with the same input keying material, the same
   payload_info, and the same G derive identical commitments, keys,
   nonces, and AADs.  The protocol_id provides cross-application domain
   separation only when it is distinct per application.  Applications
   whose keying material may be shared across systems SHOULD use an

Sullivan                 Expires 14 January 2027              [Page 102]
Internet-Draft                    raAE                         July 2026

   application-specific protocol_id (for example, "myapp-backup-v1")
   rather than a SEAL profile label.  An application adopting a SEAL
   profile (Section 4.10.2) unchanged MAY use that profile's label.

7.  IANA Considerations

   This document has no IANA actions.

   SEAL consumes identifiers from existing IANA registries: the AEAD
   Algorithms Registry ([RFC5116]) for aead_id values and the HPKE KDF
   Registry ([RFC9180] Section 7.2) for kdf_id values.  No new raAE-side
   registries are created.

   The following code points are drawn from IANA registries.  AEGIS-256
   (0x0021) and AEGIS-256X2 (0x0024) are already registered in the AEAD
   Algorithms registry per [I-D.irtf-cfrg-aegis-aead].  TurboSHAKE-256
   (0x0013) is a pending early allocation in the HPKE KDF registry per
   [I-D.ietf-hpke-pq], and its entry firms up when that draft publishes
   as an RFC.  The SEAL normative references to those drafts are
   downrefs under IRTF stream conventions until publication.

   +=================+============+================+==================+
   | Registry        | Code point | Algorithm      | Status           |
   +=================+============+================+==================+
   | AEAD Algorithms | 0x0021     | AEGIS-256      | assigned         |
   +-----------------+------------+----------------+------------------+
   | AEAD Algorithms | 0x0024     | AEGIS-256X2    | assigned         |
   +-----------------+------------+----------------+------------------+
   | HPKE KDF        | 0x0013     | TurboSHAKE-256 | early allocation |
   +-----------------+------------+----------------+------------------+

          Table 25: Registered and Early-Allocation Code Points

   Future SEAL profiles MAY consume additional entries from either
   registry without revising this document.

8.  References

8.1.  Normative References

   [I-D.ietf-hpke-pq]
              Barnes, R. and D. Connolly, "Post-Quantum and Post-
              Quantum/Traditional Hybrid Algorithms for HPKE", Work in
              Progress, Internet-Draft, draft-ietf-hpke-pq-05, 6 July
              2026, <https://datatracker.ietf.org/doc/html/draft-ietf-
              hpke-pq-05>.

Sullivan                 Expires 14 January 2027              [Page 103]
Internet-Draft                    raAE                         July 2026

   [I-D.irtf-cfrg-aegis-aead]
              Denis, F. and S. Lucas, "The AEGIS Family of Authenticated
              Encryption Algorithms", Work in Progress, Internet-Draft,
              draft-irtf-cfrg-aegis-aead-18, 5 October 2025,
              <https://datatracker.ietf.org/doc/html/draft-irtf-cfrg-
              aegis-aead-18>.

   [NIST-SP-800-38D]
              Dworkin, M., "Recommendation for Block Cipher Modes of
              Operation: Galois/Counter Mode (GCM) and GMAC",
              NIST Special Publication 800-38D, November 2007,
              <https://nvlpubs.nist.gov/nistpubs/Legacy/SP/
              nistspecialpublication800-38d.pdf>.

   [RFC2119]  Bradner, S., "Key words for use in RFCs to Indicate
              Requirement Levels", BCP 14, RFC 2119,
              DOI 10.17487/RFC2119, March 1997,
              <https://www.rfc-editor.org/rfc/rfc2119>.

   [RFC5116]  McGrew, D., "An Interface and Algorithms for Authenticated
              Encryption", RFC 5116, DOI 10.17487/RFC5116, January 2008,
              <https://www.rfc-editor.org/rfc/rfc5116>.

   [RFC5869]  Krawczyk, H. and P. Eronen, "HMAC-based Extract-and-Expand
              Key Derivation Function (HKDF)", RFC 5869,
              DOI 10.17487/RFC5869, May 2010,
              <https://www.rfc-editor.org/rfc/rfc5869>.

   [RFC8017]  Moriarty, K., Ed., Kaliski, B., Jonsson, J., and A. Rusch,
              "PKCS #1: RSA Cryptography Specifications Version 2.2",
              RFC 8017, DOI 10.17487/RFC8017, November 2016,
              <https://www.rfc-editor.org/rfc/rfc8017>.

   [RFC8174]  Leiba, B., "Ambiguity of Uppercase vs Lowercase in RFC
              2119 Key Words", BCP 14, RFC 8174, DOI 10.17487/RFC8174,
              May 2017, <https://www.rfc-editor.org/rfc/rfc8174>.

   [RFC8439]  Nir, Y. and A. Langley, "ChaCha20 and Poly1305 for IETF
              Protocols", RFC 8439, DOI 10.17487/RFC8439, June 2018,
              <https://www.rfc-editor.org/rfc/rfc8439>.

   [RFC8452]  Gueron, S., Langley, A., and Y. Lindell, "AES-GCM-SIV:
              Nonce Misuse-Resistant Authenticated Encryption",
              RFC 8452, DOI 10.17487/RFC8452, April 2019,
              <https://www.rfc-editor.org/rfc/rfc8452>.

Sullivan                 Expires 14 January 2027              [Page 104]
Internet-Draft                    raAE                         July 2026

   [RFC9180]  Barnes, R., Bhargavan, K., Lipp, B., and C. Wood, "Hybrid
              Public Key Encryption", RFC 9180, DOI 10.17487/RFC9180,
              February 2022, <https://www.rfc-editor.org/rfc/rfc9180>.

   [RFC9861]  Viguier, B., Wong, D., Ed., Van Assche, G., Ed., Dang, Q.,
              Ed., and J. Daemen, Ed., "KangarooTwelve and TurboSHAKE",
              RFC 9861, DOI 10.17487/RFC9861, October 2025,
              <https://www.rfc-editor.org/rfc/rfc9861>.

8.2.  Informative References

   [ADG22]    Albertini, A., Duong, T., Gueron, S., Kölbl, S., Luykx,
              A., and S. Schmieg, "How to Abuse and Fix Authenticated
              Encryption Without Key Commitment", USENIX Security
              2022 pp. 3291-3308, 2022,
              <https://www.usenix.org/conference/usenixsecurity22/
              presentation/albertini>.

   [BHT18]    Bose, P., Hoang, V. T., and S. Tessaro, "Revisiting AES-
              GCM-SIV: Multi-user Security, Faster Key Derivation, and
              Better Bounds", IACR ePrint 2018/136, 2018,
              <https://eprint.iacr.org/2018/136>.

   [DAE]      Rogaway, P. and T. Shrimpton, "A Provable-Security
              Treatment of the Key-Wrap Problem", EUROCRYPT 2006, LNCS
              4004, pp. 373-390, DOI 10.1007/11761679_23, 2006,
              <https://doi.org/10.1007/11761679_23>.

   [DGRW18]   Dodis, Y., Grubbs, P., Ristenpart, T., and J. Woodage,
              "Fast Message Franking: From Invisible Salamanders to
              Encryptment", CRYPTO 2018, LNCS vol. 10991, pp. 155-186,
              DOI 10.1007/978-3-319-96884-1_6, 2018,
              <https://doi.org/10.1007/978-3-319-96884-1_6>.

   [FLRR25]   Fábrega, A., Len, J., Ristenpart, T., and G. Rubin,
              "Random-Access AEAD for Fast Lightweight Online
              Encryption", EUROCRYPT 2026,
              DOI 10.1007/978-3-032-25333-0_10, 2026,
              <https://eprint.iacr.org/2025/2275>.

   [HRRV15]   Hoang, V. T., Reyhanitabar, R., Rogaway, P., and D. Vizár,
              "Online Authenticated-Encryption and its Nonce-Reuse
              Misuse-Resistance", IACR ePrint 2015/189, 2015,
              <https://eprint.iacr.org/2015/189>.

   [I-D.irtf-cfrg-aead-limits]
              Günther, F., Thomson, M., and C. A. Wood, "Usage Limits on
              AEAD Algorithms", Work in Progress, Internet-Draft, draft-

Sullivan                 Expires 14 January 2027              [Page 105]
Internet-Draft                    raAE                         July 2026

              irtf-cfrg-aead-limits-11, 4 December 2025,
              <https://datatracker.ietf.org/doc/html/draft-irtf-cfrg-
              aead-limits-11>.

   [MSetHash] Clarke, D., Devadas, S., van Dijk, M., Gassend, B., and G.
              E. Suh, "Incremental Multiset Hash Functions and Their
              Application to Memory Integrity Checking", ASIACRYPT 2003,
              LNCS 2894, pp. 188-207, DOI 10.1007/978-3-540-40061-5_12,
              2003, <https://doi.org/10.1007/978-3-540-40061-5_12>.

   [RFC8446]  Rescorla, E., "The Transport Layer Security (TLS) Protocol
              Version 1.3", RFC 8446, DOI 10.17487/RFC8446, August 2018,
              <https://www.rfc-editor.org/rfc/rfc8446>.

   [RFC8645]  Smyshlyaev, S., Ed., "Re-keying Mechanisms for Symmetric
              Keys", RFC 8645, DOI 10.17487/RFC8645, August 2019,
              <https://www.rfc-editor.org/rfc/rfc8645>.

   [RFC8937]  Cremers, C., Garratt, L., Smyshlyaev, S., Sullivan, N.,
              and C. Wood, "Randomness Improvements for Security
              Protocols", RFC 8937, DOI 10.17487/RFC8937, October 2020,
              <https://www.rfc-editor.org/rfc/rfc8937>.

   [RFC9580]  Wouters, P., Ed., Huigens, D., Winter, J., and Y. Niibe,
              "OpenPGP", RFC 9580, DOI 10.17487/RFC9580, July 2024,
              <https://www.rfc-editor.org/rfc/rfc9580>.

   [RFC9771]  Bozhko, A., Ed., "Properties of Authenticated Encryption
              with Associated Data (AEAD) Algorithms", RFC 9771,
              DOI 10.17487/RFC9771, May 2025,
              <https://www.rfc-editor.org/rfc/rfc9771>.

   [SEALPROOFS]
              Sullivan, N., "Security Analysis of the SEAL Construction:
              raAE, Snapshot Integrity, and Commitment", Work in
              Progress manuscript in preparation.

   [Tink]     Hoang, V. T. and Y. Shen, "Security of Streaming
              Encryption in Google's Tink Library", IACR ePrint
              2020/1019, 2020, <https://eprint.iacr.org/2020/1019>.

Sullivan                 Expires 14 January 2027              [Page 106]
Internet-Draft                    raAE                         July 2026

Appendix A.  Optional Plaintext-Bound Nonce Construction

   This appendix is informative.  It describes an optional encryptor-
   side construction that mixes plaintext content into the per-segment
   nonce derivation to defend against RNG state duplication.  The
   construction is encryptor-only: the decryptor reads the resulting
   nonce from the wire (in the same slot used by random mode) and never
   invokes any of the machinery below.  Implementations MAY use this
   construction in place of a fresh CSPRNG call when generating nonces
   under nonce_mode "random".  The wire format is indistinguishable from
   random mode and decryption is unaffected.

   One such construction:

   pt_digest(i) = LH(plaintext_i)

   encryption_params = [aead_id, segment_max_be, kdf_id]
   pt_hash(i) = KDF(protocol_id, pt_hash_label,
                    [pt_digest(i)],
                    encryption_params, Nh)

   nonce_ctx = encode(protocol_id, uint64(i), pt_hash(i))
   nonce(i) = KDF(protocol_id, pt_nonce_label,
                  [Random(Nn), payload_key],
                  [...payload_info, nonce_ctx], Nn)

   This construction fixes two labels:

        +==========================+================+============+
        | Derivation role          | Label variable | Value      |
        +==========================+================+============+
        | Plaintext digest binding | pt_hash_label  | "pt-nonce" |
        +--------------------------+----------------+------------+
        | Final nonce derivation   | pt_nonce_label | "nonce"    |
        +--------------------------+----------------+------------+

              Table 26: Plaintext-bound construction labels

   Both labels are distinct from all other SEAL labels.  Neither
   derivation uses nonce_base_label (Table 4): the "nonce_base" label
   belongs to derived nonce mode and does not appear in this
   construction.  In the final derivation nonce_ctx enters the info list
   as one element after the payload_info elements: its encode output is
   framed behind a length prefix like any other element, not spliced
   flat into the list.  Component vectors for both KDF classes are in
   Appendix F.19.

Sullivan                 Expires 14 January 2027              [Page 107]
Internet-Draft                    raAE                         July 2026

   LH is the over-large-field digest of Section 4.3, which runs the
   cipher suite's native KDF primitive directly on its input and returns
   Nh octets.  It is collision-resistant per Section 5.5.  Decryptors
   never compute pt_digest.

   When the encryptor's CSPRNG produces duplicated state (for example,
   from a virtual machine snapshot or a fork without reseed), two
   encryptions of distinct plaintexts at the same segment index still
   produce distinct nonces because pt_hash(i) differs.  Two encryptions
   of identical plaintexts at the same index produce identical nonces,
   resulting in deterministic encryption: the ciphertexts are identical,
   revealing only that the plaintexts are equal.  No additional
   information beyond this equality is leaked.

   For rewrites under this construction, the encryptor recomputes
   pt_hash(i) with the new plaintext, derives a fresh nonce(i) using the
   new pt_hash(i) and a new Random(Nn), seals the new plaintext under
   the new nonce, and updates the snapshot value as in Section 4.9.2.1.

   The construction is observationally equivalent to nonce_mode "random"
   at the wire format.  A decryptor cannot distinguish whether the
   encryptor used a fresh CSPRNG call or this construction to produce
   the stored nonce.  Implementations that elect to use this
   construction internally do not need to advertise it.

Appendix B.  Optional Hedged Randomness

   When a long-term symmetric key sk of at least Nh octets is available
   to the encryptor, implementations SHOULD mix it into random
   generation using the hedging pattern of [RFC8937].  If only an
   asymmetric private key is available, it MUST first be processed
   through a KDF to produce a uniform symmetric key.

   hedge_key = KDF(protocol_id, hedge_label, sk, [], Nh)

   HedgedRandom(n, label):
     return KDF(protocol_id, label,
         [hedge_key, Random(n)], [], n)

   This construction fixes one label, hedge_label = "hedge", distinct
   from all other SEAL labels.

Sullivan                 Expires 14 January 2027              [Page 108]
Internet-Draft                    raAE                         July 2026

   HedgedRandom output depends on both the CSPRNG and sk, so a weak
   CSPRNG alone cannot predict it.  Hedging does not help when the
   CSPRNG state itself is duplicated (VM snapshots, fork without
   reseed).  Identical CSPRNG output still produces identical
   HedgedRandom output.  An optional encryptor-side construction that
   mixes plaintext content into the per-segment nonce derivation to
   defend against state duplication is described in Appendix A.  That
   construction is orthogonal to hedging and the two MAY be combined.

Appendix C.  Proof Status and Security Claim Provenance

   This appendix is informative.  It states what is proven, where, and
   by whom.  Each result below is inherited from [FLRR25], argued in
   this document, or deferred to a companion proof paper in preparation
   ([SEALPROOFS]).

   Inherited from [FLRR25]:  The ra-ROR and ra-CMT games, their
      advantages, and their proofs apply to SEAL's realization of the
      base interface, summarized in Section 5.6 and Section 5.7.  The
      ra-CMT-p notion and its base proof are likewise inherited, but
      SEAL's realization of its position binding is argued here (see
      below).

   Argued in this document:  The mechanisms this document adds beyond
      [FLRR25] are argued in the subsections below.  These are the
      snapshot authenticator that realizes snapshot integrity, the nonce
      modes, the per-segment associated data and position binding of ra-
      CMT-p (bound stated here, formal accounting deferred), and the
      injectivity and domain separation of the KDF combiner.  The
      combiner argument is structural and proof-complete (Section 4.3).
      The others state their bounds.  Their full proofs are not here.

   Referenced from external work:  The capacity, rewrite, and maximum-
      object-size bounds (Section 5.9) are operational ceilings derived
      from [I-D.irtf-cfrg-aead-limits], [RFC8452] Section 9, and
      [BHT18], not security reductions proven here.

   Deferred to [SEALPROOFS]:  The formal proof of masked-multiset-hash
      snapshot integrity as an extension of the ra-ROR framework, the
      forgery and content-binding reductions of the digest transcript,
      the real-or-random treatment of the derived-nonce equality leakage
      (which relaxes that framework's nonce-respecting hypothesis to
      deterministic MRAE), and the per-segment associated data and
      position accounting of ra-CMT-p.

   Precedent, not proof:  The derived-nonce unique-nonce transform rests

Sullivan                 Expires 14 January 2027              [Page 109]
Internet-Draft                    raAE                         July 2026

      on TLS 1.3 ([RFC8446]) and STREAM ([HRRV15]) precedent, treated in
      Appendix C.6.  That precedent does not prove SEAL-RO-v1.  The KDF
      hierarchy, the arbitrary-position ra-ROR syntax, the ra-CMT
      commitment, the epoch accounting, and the object-level semantics
      remain SEAL-specific proof obligations.

   The combiner's input encoding is injective and canonical, so distinct
   (protocol_id, label, ikm, info, L) tuples map to distinct primitive
   inputs and a given tuple has one encoding across implementations
   (Section 4.3, Section 5.5).  Injectivity comes from the length-
   prefixed encoding, not from the KDF, which need not itself be
   injective.  Collision resistance is assumed only of LH.  For the KDF
   combiner's injectivity it is needed only for over-large fields.  The
   digest transcript additionally relies on LH collision resistance to
   bind each segment's ciphertext, of any length (Section 5.8.2).  The
   commitment derivation carries its own separate collision-resistance
   assumption (Appendix C.4).

   The capacity and usage limits in Section 5.9, including the maximum
   write-once object size (Section 5.9.4) and the derived nonce bounds
   (Section 5.9.7.4), are operational ceilings.  They are referenced
   from [I-D.irtf-cfrg-aead-limits], [RFC8452] Section 9 with [BHT18],
   and the 2^63 segment-index ceiling, not proven here.

C.1.  Provenance of the Security Claims

   +============+==============+========+==============================+
   |Property    |Source        |Argument| Rests on                     |
   +============+==============+========+==============================+
   |ra-ROR      |[FLRR25]      |Appendix| mu-PRF KDF (two keying       |
   |            |notion and    |C.2,    | levels), mu-ROR AEAD, no     |
   |            |base proof;   |Appendix| nonce collision, fresh per-  |
   |            |SEAL's        |C.3     | message salt                 |
   |            |arbitrary-    |        |                              |
   |            |position ra-  |        |                              |
   |            |ROR syntax is |        |                              |
   |            |a SEAL-       |        |                              |
   |            |specific      |        |                              |
   |            |obligation    |        |                              |
   +------------+--------------+--------+------------------------------+
   |ra-CMT      |[FLRR25]      |Appendix| collision resistance of the  |
   |(commitment,|              |C.4     | commitment derivation map    |
   |position-   |              |        | over (protocol_id, "commit", |
   |respecting) |              |        | CEK, payload_info, G,        |
   |            |              |        | commitment_length)           |
   +------------+--------------+--------+------------------------------+
   |ra-CMT-p    |[FLRR25]      |Appendix| ra-CMT collision bound plus  |
   |(per-segment|notion and    |C.4     | the underlying AEAD's        |

Sullivan                 Expires 14 January 2027              [Page 110]
Internet-Draft                    raAE                         July 2026

   |associated- |base proof;   |        | commitment bound for the     |
   |data and    |SEAL          |        | forged position              |
   |position    |position-     |        |                              |
   |commitment) |binding       |        |                              |
   |            |realization   |        |                              |
   |            |and per-      |        |                              |
   |            |segment       |        |                              |
   |            |associated-   |        |                              |
   |            |data and      |        |                              |
   |            |position      |        |                              |
   |            |accounting    |        |                              |
   |            |argued here,  |        |                              |
   |            |formal write- |        |                              |
   |            |up in         |        |                              |
   |            |[SEALPROOFS]  |        |                              |
   +------------+--------------+--------+------------------------------+
   |KDF combiner|this document |Section | injectivity of the length-   |
   |injectivity |(structural,  |4.3,    | prefixed encoding (KDF need  |
   |and domain  |proof-        |Section | not be injective); collision |
   |separation  |complete)     |5.5     | resistance of LH for over-   |
   |            |              |        | large fields only            |
   +------------+--------------+--------+------------------------------+
   |Snapshot    |this          |Appendix| mu-PRF KDF under snap_key;   |
   |integrity   |document,     |C.5     | MAC unforgeability of        |
   |(masked     |Appendix C.5  |        | snapshot_tag over (n_seg,    |
   |multiset    |              |        | acc); deterministic-masking  |
   |hash)       |              |        | birthday and mix-and-match   |
   |            |              |        | terms; per-object separation |
   |            |              |        | via salt-bound snap_key      |
   +------------+--------------+--------+------------------------------+
   |Snapshot    |this          |Section | mu-PRF KDF under snap_key    |
   |integrity   |document,     |5.8.2   | (single fresh-input forgery  |
   |(digest     |Section       |        | term, no mask or birthday);  |
   |transcript) |5.8.2; formal |        | collision resistance of LH   |
   |            |write-up in   |        | binding each ciphertext      |
   |            |[SEALPROOFS]  |        | leaf; per-object separation  |
   |            |              |        | via salt-bound snap_key and  |
   |            |              |        | the commitment element       |
   +------------+--------------+--------+------------------------------+
   |Snapshot    |this          |Section | as the digest transcript,    |
   |integrity   |document,     |5.8.3   | with LH collision resistance |
   |(epoch      |Section       |        | applied at the epoch and     |
   |digest tree)|5.8.3; formal |        | root levels of the two-level |
   |            |write-up in   |        | fold                         |
   |            |[SEALPROOFS]  |        |                              |
   +------------+--------------+--------+------------------------------+
   |AEAD usage, |external work |Section | [I-D.irtf-cfrg-aead-limits], |
   |rewrite, and|(operational, |5.9,    | [RFC8452] Section 9 with     |

Sullivan                 Expires 14 January 2027              [Page 111]
Internet-Draft                    raAE                         July 2026

   |max-object  |not proven    |Section | [BHT18], 2^63 segment-index  |
   |bounds      |here)         |5.9.4,  | ceiling                      |
   |            |              |Section |                              |
   |            |              |5.9.7.4 |                              |
   +------------+--------------+--------+------------------------------+
   |Derived-    |precedent,    |Section | write-once uniqueness        |
   |nonce       |not proof     |4.5.3.2,| discipline reducing to       |
   |unique-nonce|(TLS 1.3      |Appendix| nonce-respecting AEAD        |
   |transform   |[RFC8446],    |C.6     | security                     |
   |            |STREAM        |        |                              |
   |            |[HRRV15])     |        |                              |
   +------------+--------------+--------+------------------------------+
   |Derived-    |this          |Appendix| MRAE AEAD ([RFC8452]); per-  |
   |nonce       |document;     |C.6     | segment fixed-nonce data-    |
   |equality    |full proof in |        | volume cap                   |
   |leakage     |[SEALPROOFS]  |        |                              |
   |(MRAE)      |              |        |                              |
   +------------+--------------+--------+------------------------------+

                Table 27: Provenance of the security claims

C.2.  Advantage Notation and Adversary Parameters

   For a ra-ROR adversary (Section 5.4) using at most u distinct CEKs
   and q_m messages, each encrypted with an independent fresh 32-octet
   per-content salt (Section 4.9.1.1), across E total epochs and making
   q_e encryption and q_d decryption segment queries with sigma = q_e +
   q_d, the advantage is bounded by:

   Adv_raROR(A) <= Adv_salt_coll
                      + Adv_muPRF(KDF, B_KDF)
                      + Adv_muROR(AEAD, B_AEAD)
                      + Adv_nonce_coll
                      + Adv_acc

   The terms in the bound are as follows.

   Adv_salt_coll  The probability that two messages encrypted under one

Sullivan                 Expires 14 January 2027              [Page 112]
Internet-Draft                    raAE                         July 2026

      CEK draw the same salt.  Because the salt is a 32-octet value
      drawn uniformly at random, this term is at most q_m^2 / 2^256.  A
      salt collision is not a PRF-distinguishing event: the KDF
      deterministically produces identical payload_key, snap_key, and
      nonce_base outputs whenever (CEK, payload_info) repeats, so the
      event must be charged separately.  Conditioned on no salt
      collision, distinct messages under one CEK have distinct
      payload_info values and therefore independent KDF outputs under
      the mu-PRF assumption.  This term holds only under the fresh per-
      message uniform salt requirement of Section 4.9.1.1.  A profile
      that reuses salts or draws them non-uniformly forfeits it and must
      argue payload-schedule separation by other means (Section 6.7).

   Adv_muPRF(KDF, B_KDF)  The multi-user PRF advantage of the KDF,
      covering two keying levels.  Level A is keyed by the CEK and
      produces payload_key, snap_key, and nonce_base.  It has u users,
      queried once per message.  Level B is keyed by payload_key
      (producing epoch_key per Section 4.5.2 and, in the plaintext-bound
      construction, per-segment nonces per Appendix A) and by snap_key
      (producing the snapshot authenticator's keyed derivations).  It
      has q_m users.  The snap_key subset of Level B feeds the
      configured snapshot authenticator and is counted under Adv_acc
      below.  It is not counted again here.

   Adv_muROR(AEAD, B_AEAD)  The multi-user real-or-random advantage of
      the AEAD over sigma total segment queries.  There is one mu-ROR
      user per distinct segment key, for E epoch keys total (E = q_m
      when each message uses a single flat epoch key).  This advantage
      absorbs a key-collision birthday term of about E^2/2^(8*Nk) over
      the E distinct keys.  For the 256-bit-key suites (Nk = 32) that
      term is about E^2/2^256 and is negligible.  For AES-128-GCM (Nk =
      16) it is about E^2/2^128 and is the binding epoch-key collision
      floor.  Section 5.9.6 bounds E for that suite.

   Adv_nonce_coll  The probability of a nonce collision under any
      segment key.  In derived nonce mode it is zero, conditional on no
      salt collision (already charged via Adv_salt_coll).  In random
      nonce mode (with or without the plaintext-bound hedge) it is
      bounded per Section 5.9.7.1.  The derived-mode statement relies on
      the deterministic-MRAE analysis in Appendix C.6.

   Adv_acc  The masked multiset hash's snapshot forgery advantage,
      present only under snap_id 0x0001 and zero otherwise.  SEAL
      authenticates the XOR accumulator (Section 4.7) with a snapshot
      tag, a MAC under snap_key, and masks the published accumulator
      with a deterministic tag-derived pad.  A different snapshot
      authenticator contributes its own term in place of Adv_acc: the
      digest transcript (snap_id 0x0002) contributes a single fresh-

Sullivan                 Expires 14 January 2027              [Page 113]
Internet-Draft                    raAE                         July 2026

      input PRF forgery term, with no birthday or mix-and-match
      component (Section 5.8.2).  The epoch digest tree (snap_id 0x0003)
      contributes the same fresh-input PRF forgery term.  The key-holder
      content binding of either authenticator is a separate notion from
      this without-key term, resting on LH collision resistance
      (Section 5.8.2, Section 5.8.3), and is not counted here.

   The Adv_acc term is bounded by:

Adv_acc <= Adv_muPRF_acc
           + q_s^2 / 2^(8*Nh)          (snapshot-collision birthday)
           + q_v / 2^(8*Nh)            (fresh-input tag forgery)
           + q_s * q_v / 2^(8*Nh)      (mix-and-match accumulator guess)

   Here Adv_muPRF_acc is the snap_key subset of B_KDF's Level B queries,
   counted here and not again in Adv_muPRF(KDF, B_KDF).  q_s is the
   number of published snapshot states the adversary observes and q_v
   the number of SnapVerify queries it makes.  The q_s^2 birthday term
   is the cost of the deterministic masking (Appendix C.5).  The
   dominant terms are that birthday and the mix-and-match q_s * q_v /
   2^(8*Nh), and for the SEAL suites (8*Nh of 256, 384, or 512) every
   term is negligible.

C.3.  ra-ROR Reduction

   The reduction follows the [FLRR25] hybrid over the two-level key
   schedule (Section 4.5).  Condition on no salt collision
   (Adv_salt_coll).  Replace the CEK-keyed KDF outputs, then the
   payload_key- and snap_key-keyed outputs, with uniformly random values
   (Adv_muPRF at the two keying levels).  Bound the resulting segment
   AEAD outputs (Adv_muROR).  A flat epoch key is the single-epoch case
   of the same argument.  The segment AEAD calls are nonce-respecting
   only under no nonce collision, the event charged to Adv_nonce_coll
   (Appendix C.2).  In derived nonce mode this term is zero conditional
   on the same no-salt-collision event (Appendix C.6).  The snapshot
   authenticator's forgery enters separately as Adv_acc (Section 5.8).
   The reduction constructs adversaries B_KDF against multi-user PRF
   security of the KDF and B_AEAD against multi-user real-or-random
   security of the AEAD, each running in time approximately that of A.

C.4.  Commitment

   ra-CMT security reduces to collision resistance of the commitment
   derivation map over the tuple (protocol_id, "commit", CEK,
   payload_info, G, commitment_length), with G empty by default.  PRF
   security alone is not sufficient for this reduction, because the
   commitment adversary may choose the CEK and the context values.  The
   collision-resistance assumption captured in Section 4.5 is what makes

Sullivan                 Expires 14 January 2027              [Page 114]
Internet-Draft                    raAE                         July 2026

   the reduction sound.

   For commitment_length = L octets, the relevant quantities are:

   fixed-pair collision probability:  2^(-8*L)
   q-query birthday probability:      q^2 / 2^(8*L + 1)
   collision-search work factor:      about 2^(4*L)

   Thus 16 octets gives about 2^64 collision-search work, 32 octets
   gives about 2^128 (capped by SHA-256's own 2^128 collision resistance
   for HKDF-SHA-256), 48 octets gives about 2^192 (capped by SHA-384's
   2^192 for HKDF-SHA-384), and 64 octets gives about 2^256 (for HKDF-
   SHA-512, capped by SHA-512's 2^256, and for TurboSHAKE-256).  The
   fixed-pair collision probabilities at 32, 48, and 64 octets are
   2^(-256), 2^(-384), and 2^(-512), respectively.

   The ra-CMT-p advantage is bounded by the sum of two terms: the
   commitment collision bound above and the underlying AEAD's commitment
   bound for the forged position.

   Position binding for ra-CMT-p is inherited from the underlying AEAD's
   commitment level (Section 5.7).  The notion and base proof are
   inherited from [FLRR25].  The exact per-segment associated data and
   position accounting is deferred to [SEALPROOFS].

C.5.  Snapshot Authenticator

   SEAL's snapshot authenticator (snap_id 0x0001) is the MSet-XOR-Hash
   of Clarke et al.  ([MSetHash]) with a deterministic mask this
   document adds.  Each segment contributes a keyed KDF evaluation of
   its index and tag, the contributions XOR into an accumulator, and the
   published value masks that accumulator under snap_key.  A different
   authenticator that meets the requirement of Section 5.5 carries its
   own argument.

   The adversary has full read and write access to the stored segments,
   their metadata, and the snapshot, and wins by making SnapVerify
   accept a (segment set, snapshot) pair other than the writer's current
   state.  Whole-object rollback to an earlier honest state is excluded
   and is the application's freshness responsibility (Section 6.6).

Sullivan                 Expires 14 January 2027              [Page 115]
Internet-Draft                    raAE                         July 2026

   Publishing the accumulator in the clear would be insecure.  That is
   the MSet-XOR-Hash with its mask removed, which is only set-collision
   resistant.  A write adversary reads the accumulator across honest
   rewrites, collects the contribution differences old_contrib XOR
   new_contrib, and after more than 8*Nh of them solves a GF(2) system
   for a subset of segments it can revert without changing the
   accumulator.  The current snapshot tag still verifies, so a non-
   historical mixture of versions is accepted with no MAC forgery.

   The deterministic mask defeats this attack.  Here wrapped_acc = acc
   XOR snapmask(n_seg, snapshot_tag) hides the accumulator behind a one-
   time pad keyed by snap_key and seeded by the synthetic snapshot tag,
   the synthetic-IV derandomization of deterministic authenticated
   encryption ([DAE]).  Two published states that collide on (n_seg,
   acc), or on the snapshot tag at equal count, would expose a raw
   accumulator difference.  Over q_s published states that costs a
   birthday term q_s^2 / 2^m, with m = 8*Nh.  Off that event the masks
   are independent one-time pads, the published transcript is
   independent of every accumulator, and the recombination above has no
   linear system left to solve.  Security reduces to the PRF security of
   the KDF.  The mask protects the accumulator only against a verifier
   that returns one bit, which is why SnapVerify does not surface the
   recovered accumulator (Section 4.7.4).

   A forgery is then one of two events.  Either a fresh snapshot-tag
   input is guessed, a MAC forgery bounded by q_v / 2^m over q_v
   verifications, or a different segment set is made to hit a published
   accumulator by chance, a mix-and-match bounded by q_s * q_v / 2^m
   against q_s published states.  The mix-and-match term is a set
   collision on the keyed contribution function and relies on the set-
   collision resistance of the MSet-XOR-Hash-style construction
   ([MSetHash]).  The formal bound for this term is discharged in
   [SEALPROOFS].  Collecting the terms, the snapshot forgery advantage
   Adv_acc of Appendix C.2 is

   Adv_acc <= Adv_muPRF_acc + q_s^2 / 2^m + q_v / 2^m + q_s * q_v / 2^m

   with m = 8*Nh and dominant terms q_s^2 / 2^m and q_s * q_v / 2^m,
   both negligible at Nh of 32, 48, or 64.  The argument relies on
   segment AEAD authenticity, already charged as Adv_muROR, and adds no
   term beyond Adv_acc.  Per-object separation comes from the salt-bound
   snap_key, and the authenticator needs no assumption beyond the multi-
   user PRF the key schedule already uses.  It does not need the
   commitment's collision resistance.  Integrating this reduction into
   the combined ra-ROR proof is in preparation ([SEALPROOFS]).

Sullivan                 Expires 14 January 2027              [Page 116]
Internet-Draft                    raAE                         July 2026

C.6.  Nonce Modes

   The unique-nonce transform of Section 4.5.3.2, which XORs the segment
   index and finality bit into nonce_base, follows TLS 1.3 ([RFC8446])
   static-IV-XOR-identifier and STREAM ([HRRV15]) counter-plus-final-bit
   precedent.  Under the write-once uniqueness discipline every derived
   nonce is distinct, which reduces unique-nonce record protection to
   nonce-respecting AEAD security.  This is precedent, not a SEAL-
   specific proof.

   In derived nonce mode the nonce-collision term is zero conditional on
   no salt collision, because each segment index maps to one derived
   nonce and a fresh per-message salt makes the segment keys distinct
   across messages.  A rewrite reuses a segment's derived nonce, so the
   construction relies on the underlying MRAE AEAD: re-encrypting the
   same plaintext and associated data under the same key and nonce
   reproduces the same ciphertext, which leaks only equality of those
   inputs, while distinct inputs remain real-or-random secure
   ([RFC8452]).  The formal real-or-random treatment of this equality
   leakage for derived nonce mode is in preparation ([SEALPROOFS]).
   That treatment builds on the [FLRR25] ra-ROR analysis and changes its
   nonce-respecting hypothesis: a rewrite repeats a segment's derived
   nonce, so the proof replaces the nonce-respecting AEAD assumption
   with deterministic-MRAE security, under which nonce reuse leaks only
   input equality.

Appendix D.  Design Rationale

   This appendix is informative.

D.1.  Key Schedule Design Rationale

   The per-content salt makes the payload schedule unique even when a
   CEK is reused across messages, which matters for applications that
   derive CEKs from group keys.  The encryptor chooses the salt locally
   at write time.  A per-message counter would instead require
   synchronized state.

   The CEK is 32 octets regardless of the AEAD key size Nk, so a 128-
   bit-key AEAD still derives its keys from a 256-bit CEK.

   Epoch keys bound the number of AEAD invocations under any one segment
   encryption key.  Several considerations motivate this.

   AEAD per-key bounds:  The per-key bounds of the underlying AEAD
      (birthday for 96-bit-nonce schemes, block-size for AES, integrity
      forgery for all schemes) become the limiting factor on security
      long before the nonce space is fully exhausted.

Sullivan                 Expires 14 January 2027              [Page 117]
Internet-Draft                    raAE                         July 2026

   Write-once content:  This matters even for content written once:
      large content with many segments can place many AEAD invocations
      under a single key.

   Rewrites:  Rewrites consume additional invocations and can exhaust
      the same budget faster than write-once use, but they are not the
      only reason for epoching.  Write-once large content benefits from
      the same partitioning.

   Random access:  Rekeying the entire content would defeat the random-
      access property, so epoch keys bound the per-key invocation count
      without requiring a full re-encryption.

   Labels separate derivation roles: commitment and payload_key share
   the same inputs but different labels, making them independent under
   the PRF assumption.  Once CEK and salt are chosen the hierarchy is
   fixed, with no mutable state to synchronize across writers.

D.2.  Nonce Mode Design Rationale

   Random mode is simplest but trusts the CSPRNG completely.  Derived
   mode removes that trust.  In a mutable profile it requires an MRAE
   AEAD, because an in-place rewrite reuses the segment nonce
   (Section 6.5).  In the immutable profile SEAL-RO-v1 the write-once
   rule keeps every derived nonce unique, so derived mode pairs with any
   AEAD.  Table 10 gives the default nonce_mode each suite uses in the
   mutable profile, and a profile MAY select another valid (nonce_mode,
   snap_id) tuple.

D.3.  Snapshot Authenticator Design Rationale

   Three shapes were considered for whole-object integrity over an
   updatable segment set.  A MAC over the concatenated tag list is the
   simplest, but a rewrite changes one tag in the middle of the input,
   so the writer recomputes over all n_seg tags on every update.  A
   Merkle tree over the tags updates in O(log n) and can offer per-
   segment inclusion proofs, but it either stores interior nodes that
   grow with the object or re-reads segment tags to rebuild paths, and
   it rests on collision resistance of a hash.  The masked multiset hash
   updates in O(1) per rewrite, stores a single value of 2*Nh octets
   regardless of object size, needs no per-segment proofs, and needs
   only the multi-user PRF assumption the key schedule already carries.
   Its costs are the ones this document states explicitly: the
   accumulator stays masked, the verifier returns a single bit
   (Section 4.7.4), and the deterministic mask adds the q_s^2 birthday
   term to the bound (Appendix C.5).  A profile that needs per-segment
   inclusion proofs or third-party verifiability needs a different
   authenticator under its own snap_id (Section 4.7.1).

Sullivan                 Expires 14 January 2027              [Page 118]
Internet-Draft                    raAE                         July 2026

   The accumulator is on the wire so a stateless writer can resume the
   O(1) update from the stored snapshot alone, rather than holding it in
   trusted state or re-reading every tag.  Publishing it is why the mask
   and the one-bit verifier are needed: an exposed accumulator would let
   a write adversary recombine observed values (Appendix C.5).

   The digest transcript verifies one segment only by recomputing over
   every leaf, so a reader of one segment in a large object still reads
   the whole leaf list.  The epoch digest tree (Section 4.7.6) folds the
   leaves into per-epoch heads and the heads into the snapshot, so a
   reader verifies one segment from two aligned metadata reads, each one
   segment_max or less: the target epoch's leaf run and the list of
   epoch heads.  That is the cost it saves the digest transcript on
   large content read at random.

   The epoch digest tree has two levels but is not a Merkle tree.
   Verification reconstructs an inclusion proof for a segment,
   recomputing its leaf, then its epoch head from the epoch's leaves,
   then the root from the epoch heads, rather than transmitting a
   compact authentication path.  Recomputing an epoch head reads the
   epoch's leaf run as one contiguous aligned read, while a Merkle
   authentication path would read a logarithmic set of scattered sibling
   nodes.  On the aligned layout, where a reader seeks in segment-sized
   units, one contiguous aligned read beats a scattered logarithmic path
   whenever the run fits one read.  The same holds one level up for the
   list of epoch heads.

   The two random-access reads trade off against epoch_length.  The
   epoch leaf run, 2^r * meta_len, grows with r, while the epoch-heads
   region, ceil(n_seg / 2^r) * Nh, shrinks with r as the epochs get
   fewer and larger.  The largest r that keeps the leaf run within one
   aligned read therefore also makes the epoch-heads region as small as
   it can be, which pushes the content-size ceiling as high as it goes.
   At that point the two reads are each about one aligned read, so
   neither dominates.  A smaller r shrinks the leaf read but enlarges
   the head region and lowers the ceiling, and a larger r overflows the
   leaf read.  The rule in Section 4.7.6 selects that balance point.

   The list of epoch heads is E * Nh octets and stays one aligned read
   up to a content-size ceiling: about 2 GiB at segment_max 16384 and
   about 128 GiB at 65536.  The ceiling is a read-efficiency threshold,
   not a size limit.  Past it the head list spans a few aligned reads
   and grows slowly, and the ceiling scales about cubically with
   segment_max, so a larger segment size moves it far out at the cost of
   coarser random-access granularity.  A tree over the epoch heads would
   keep a single bounded read at any size but is out of scope.  The two-
   read regime already covers the write-once content this authenticator
   targets.

Sullivan                 Expires 14 January 2027              [Page 119]
Internet-Draft                    raAE                         July 2026

Appendix E.  SEAL-simple Implementation Sketch

   This appendix is informative.  It specializes Section 4.9.1.1 at
   cipher suite (aead_id = 0x0002, kdf_id = 0x0001) for the SEAL-simple
   named instantiation (Section 4.12), so an implementer of only that
   profile can build a conforming encryptor and decryptor from one
   block.

   +===================+==============================================+
   | Field             | Value                                        |
   +===================+==============================================+
   | protocol_id       | "SEAL-RO-v1"                                 |
   +-------------------+----------------------------------------------+
   | aead_id           | 0x0002 (AES-256-GCM)                         |
   +-------------------+----------------------------------------------+
   | kdf_id            | 0x0001 (HKDF-SHA-256)                        |
   +-------------------+----------------------------------------------+
   | snap_id           | 0x0000 (no snapshot authenticator)           |
   +-------------------+----------------------------------------------+
   | nonce_mode        | 0x01 (derived)                               |
   +-------------------+----------------------------------------------+
   | epoch_length      | 32                                           |
   +-------------------+----------------------------------------------+
   | segment_max       | 65536                                        |
   +-------------------+----------------------------------------------+
   | Nk                | 32                                           |
   +-------------------+----------------------------------------------+
   | Nn                | 12                                           |
   +-------------------+----------------------------------------------+
   | Nt                | 16                                           |
   +-------------------+----------------------------------------------+
   | Nh                | 32                                           |
   +-------------------+----------------------------------------------+
   | commitment_length | 32                                           |
   +-------------------+----------------------------------------------+
   | layout            | linear, immutable reduction (Section 4.11.4) |
   +-------------------+----------------------------------------------+

      Table 28: Fixed parameters for SEAL-simple(HKDF-SHA-256, AES-
                                 256-GCM)

   Per-segment associated data A_i is empty throughout this sketch, and
   a caller that needs a non-empty A_i follows the general segment-AAD
   form defined in Section 4.4.2.

   Three separate quantities bound how large an object under this
   profile can grow.

Sullivan                 Expires 14 January 2027              [Page 120]
Internet-Draft                    raAE                         July 2026

   *  Integrity: 512 ZiB (2^79 octets) of ciphertext has total forgery
      probability at most ~2^-52, from the AES-256-GCM tag-forgery
      formula 2 x v x (L + 1) / 2^128 at v = 2^63 segments and L = 2^12
      blocks per segment (Section 5.9).

   *  Confidentiality: 8 YiB (2^83 octets) is the AES-256-GCM budget
      across all 2^31 epoch keys, each held to a 2^-32 advantage (2^52
      octets per key).  A full 512 ZiB (2^79-octet) object uses 1/16 of
      that, a ratio fixed by the SEAL-simple choice of epoch_length 32.

   *  Structural: 512 ZiB (2^79 octets) is the object ceiling, from
      derived-nonce framing capping the segment index at 2^63 across up
      to 2^31 epoch keys (Section 4.5.3.2, Section 5.9.4).

   In the block below, each KDF(...) call denotes the two-step KDF
   combiner of Section 4.3 evaluated at protocol_id = "SEAL-RO-v1",
   whose leading argument is the derivation label.

  inputs:  CEK  (32 octets)
           salt (32 octets, fresh uniformly random draw per object;
                 see {{full-encryption}}, {{derived-nonces}})
           G    (octet string, empty by default)
           P_0, ..., P_{n-1}
                (segment plaintexts;  every non-final P_i is exactly
                 65536 octets, only the final P_i may be shorter)

  ;;---- payload_info ----

  payload_info = [uint16(0x0002),  ;; aead_id
                  uint32(65536),   ;; segment_max_be
                  uint16(0x0001),  ;; kdf_id
                  uint16(0x0000),  ;; snap_id
                  uint8(0x01),     ;; nonce_mode = derived
                  uint8(32),       ;; epoch_length_u8
                  salt]            ;; 32 octets

  ;;---- message schedule ----

  commitment  = KDF("commit",      [CEK], [...payload_info, G], 32)
  payload_key = KDF("payload_key", [CEK], payload_info,         32)
  nonce_base  = KDF("nonce_base",  [CEK], payload_info,         12)

  ;;---- per segment ----

  for i in 0 .. n-1:
      is_final    = 1 if i = n-1 else 0
      epoch_index = i >> 32                       ;; epoch_length = 32
      segment_key = KDF("epoch_key",

Sullivan                 Expires 14 January 2027              [Page 121]
Internet-Draft                    raAE                         July 2026

                        [payload_key], [uint64(epoch_index)], 32)

      nonce_i     = nonce_base[0:4]
                    || (nonce_base[4:12]
                        XOR uint64((i << 1) | is_final))

      (ct_i || tag_i) = AES-256-GCM.Seal(segment_key, nonce_i, "", P_i)

  ;;---- stored object ----

  stored_object = salt || commitment
                  || (ct_0 || tag_0)
                  || (ct_1 || tag_1)
                  || ...
                  || (ct_{n-1} || tag_{n-1})

  ;;---- decrypt ----

  ;; Parse salt (32 octets), commitment (32 octets), and n segments from
  ;; stored_object.  Every non-final ct_i is exactly segment_max octets
  ;; and only the final ct_i may be shorter ({{linear-layout}}).

  if KDF("commit", [CEK], [...payload_info, G], 32) != commitment:
      return commitment error   ;; wrong CEK, parameter set, or G

  for i in 0 .. n-1:
      ;; segment_key, nonce_i, is_final derived exactly as in the
      ;; encrypt loop.
      P_i = AES-256-GCM.Open(segment_key, nonce_i, "", ct_i || tag_i)
            (return a decryption error on AEAD verification failure)

  ;; The reader MUST reject the object if segment n-1 did not open under
  ;; is_final = 1 ({{full-decryption}}).

   The general algorithm's snap_key derivation is not used at snap_id =
   0x0000 and an implementer of this profile omits it.

   See Appendix F.23 for the corresponding byte-level test vector.  A
   conforming implementation reproduces its commitment, payload_key, and
   nonce_base, and the per-segment nonce, ciphertext, and tag, from the
   fixed inputs of that vector.

Appendix F.  Test Vectors

   This appendix is informative.

Sullivan                 Expires 14 January 2027              [Page 122]
Internet-Draft                    raAE                         July 2026

   All vectors except Appendix F.23 use protocol_id = "SEAL-RW-v1";
   Appendix F.23 uses "SEAL-RO-v1".  Every vector uses CEK = 32 octets
   of 0xAA and salt = 32 octets of 0x04.  Each block opens with its
   parameter set, grouped into three buckets: the cipher suite (aead_id,
   kdf_id), the geometry (segment_max), and the operational parameters
   (epoch_length, nonce_mode, snap_id).  aead_id and kdf_id are the
   2-octet IANA code points from Table 10 and Table 11.  Then come the
   payload_info elements as encoded on the wire, the payload schedule
   outputs, the per-segment values, and the snapshot fields.
   Hexadecimal values wrap at 16 octets per line.

   The snapshot fields are the internal accumulator acc, the mask
   snapmask(n_seg, snapshot_tag), the published wrapped_acc = acc XOR
   mask, and the snapshot tag.  The snapshot stored on the wire is
   wrapped_acc || snapshot_tag (Section 4.7.4).  The accumulator is an
   intermediate value and is not on the wire, and the count is recovered
   from the segment set.

   Single-segment plaintexts are "Hello, SEAL!" (12 octets).  Two-
   segment messages append "Two segments of SEAL" (20 octets) as the
   final segment.  Most vectors use nonce_mode "random" with stored
   nonces 0x03 and 0x07 repeated to the AEAD nonce length.  The derived-
   nonce vector in Appendix F.17 instead recomputes each nonce from
   nonce_base, and the cross-epoch vector in Appendix F.18 sets
   epoch_length 0 and exposes the intermediate epoch_key and segment_key
   for each segment.  Appendix F.1 gives the full KDF trace for one
   commitment.  The other blocks list schedule outputs only.  The
   nineteen computed positive vectors, plus the negative SnapVerify
   vector in Appendix F.22, are published byte-for-byte as raae-
   v1-vectors.json in the draft repository.

   The vectors are organized by purpose:

   Annotated walkthrough:  Appendix F.1 shows one complete HKDF-SHA-256
      trace, including the commitment KDF inputs.  Use it to debug
      framing, payload_info construction, commitment derivation, segment
      AAD, accumulator contribution, and snapshot_tag computation.

   Combiner injectivity coverage:  Appendix F.3 exercises the KDF
      combiner in isolation and demonstrates encode() injectivity,
      including the empty-sequence and same-octets-different-grouping
      cases.  The 38 combiner vectors are published as raae-v1-combiner-
      kdf-vectors.json.

   Cipher-suite coverage:  The single-segment and two-segment vectors
      exercise the listed AEAD and segment-size combinations with HKDF-
      SHA-256.  Use these to check AEAD code points, segment_max
      encoding, nonce storage, and finality-bit handling.

Sullivan                 Expires 14 January 2027              [Page 123]
Internet-Draft                    raAE                         July 2026

   KDF coverage:  Appendix F.15 covers the TurboSHAKE-256 KDF suite and
      its 64-octet Nh outputs.

   Rewrite coverage:  Appendix F.16 shows a segment rewrite and the
      corresponding accumulator and snapshot_tag update.

   Derived-nonce coverage:  Appendix F.17 covers AES-256-GCM-SIV in
      derived nonce mode, where the nonce is recomputed rather than
      stored.

   Epoch coverage:  Appendix F.18 shows epoch_length = 0, the finest
      rotation, and exposes per-segment epoch_key and segment_key
      values.  The AEGIS-256 blocks exercise the opposite endpoint, the
      flat key at epoch_length = 63.

   Plaintext-bound nonce coverage:  Appendix F.19 exposes the component
      values of the optional plaintext-bound nonce construction, which
      no end-to-end vector can reach.

   Global associated data coverage:  Appendix F.2 pins the commitment's
      G input at its empty default, which equals the Appendix F.1
      commitment, and at a nonempty value.

   Empty-AAD coverage:  All vectors use empty per-segment A_i.  As
      specified in Section 4.4.2, an empty A_i is omitted from the
      encoding rather than encoded as a zero-length fourth element.

   Negative coverage:  Appendix F.22 changes the accumulator without
      recomputing the snapshot tag.  SnapVerify rejects the stored
      snapshot.

   The JSON file is the complete corpus for automated tests.  The text
   below is intended for debugging and review.

F.1.  Single Segment, AES-256-GCM, HKDF-SHA-256, 16384

   Parameter set:
     cipher suite:
       aead_id       0x0002  (AEAD_AES_256_GCM)
       kdf_id        0x0001  (HKDF-SHA-256)
     geometry:
       segment_max   16384
     operational:
       epoch_length  1
       nonce_mode    random
       snap_id       0x0001  (masked multiset hash)

   payload_info (the KDF frames each element):

Sullivan                 Expires 14 January 2027              [Page 124]
Internet-Draft                    raAE                         July 2026

     aead_id         ( 2 octets): 0002
     segment_max_be  ( 4 octets): 00004000
     kdf_id          ( 2 octets): 0001
     snap_id         ( 2 octets): 0001
     nonce_mode      ( 1 octets): 00
     epoch_length_u8 ( 1 octets): 01
     salt            (32 octets):
       04040404040404040404040404040404
       04040404040404040404040404040404

   Payload schedule:
     commitment   (32 octets):
       47ea0ec7409b9b95d676019917a19f1c
       5831eb236aba459063458e525d130d0c
     payload_key  (32 octets):
       c1f2663e99977428dc0fec1566ce15e9
       1398634ab9b1d004945de48560707062
     snap_key     (32 octets):
       bc314ecaa8ff6c1c4ebc13b54597a10d
       2bcf412b40c428a0e411a828fcfb52ef

   Segment 0 (is_final=1):
     nonce        (12 octets):
       030303030303030303030303
     segment_aad  (24 octets):
       00095345414c2d444154410008000000
       0000000000000101
     ciphertext   (12 octets):
       6a2b84e72ce8edbf4259eebd
     tag          (16 octets):
       a821834621b302b0eb00b0245fff2efb
     contrib      (32 octets):
       97b74013b135f6fe1739da05e1720b90
       dc596a6d09e29bfe437bb710391c9ba8

   accumulator  (32 octets):
       97b74013b135f6fe1739da05e1720b90
       dc596a6d09e29bfe437bb710391c9ba8
   mask         (32 octets):
       9356a1f7905b40b4561315a6892503d6
       2b0c7162aa19285f939325709f5847fb
   wrapped_acc  (32 octets):
       04e1e1e4216eb64a412acfa368570846
       f7551b0fa3fbb3a1d0e89260a644dc53
   snapshot_tag (32 octets):
       4e7bb00b4216798e02e511b26f0167c2
       a3f6c791407994e1f503f3923e591438

Sullivan                 Expires 14 January 2027              [Page 125]
Internet-Draft                    raAE                         July 2026

   KDF trace for the commitment (HKDF-SHA-256):
     extract_input = encode(protocol_id, "commit", CEK):
       000a5345414c2d52572d76310006636f
       6d6d69740020aaaaaaaaaaaaaaaaaaaa
       aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
       aaaaaaaaaaaa
     prk = HKDF-Extract(salt = protocol_id, extract_input):
       8eb0007f1e8ac96904742b4fb4448aba
       9d8a4b319f72f7e4a6d7bb8aab95737d
     expand_info = encode(protocol_id, "commit", ...payload_info, G,
                          I2OSP(32, 2)):
       000a5345414c2d52572d76310006636f
       6d6d6974000200020004000040000002
       00010002000100010000010100200404
       04040404040404040404040404040404
       04040404040404040404040404040000
       00020020
     commitment = HKDF-Expand(prk, expand_info, 32):
       47ea0ec7409b9b95d676019917a19f1c
       5831eb236aba459063458e525d130d0c

F.2.  Commitment with Global Associated Data

   These vectors pin the G input (Section 4.6) against the schedule of
   Appendix F.1: the same CEK, salt, and payload_info with G at its
   empty default and at a nonempty value.  The default case equals the
   Appendix F.1 commitment, since every commitment includes the G
   element.  These values are printed here only and are not part of the
   end-to-end corpus.

   G default (empty):
     commitment   (32 octets):
       47ea0ec7409b9b95d676019917a19f1c
       5831eb236aba459063458e525d130d0c
     (the Single Segment, AES-256-GCM, HKDF-SHA-256, 16384
      commitment)

   G = "raae-demo-g":
     G            (11 octets):
       726161652d64656d6f2d67
     commitment   (32 octets):
       d8eedb1fa0f77428cc33d252eb307796
       ae3bb911c2f6ea7a9e5b0bde312afd73

Sullivan                 Expires 14 January 2027              [Page 126]
Internet-Draft                    raAE                         July 2026

F.3.  KDF Combiner Vectors

   These vectors exercise the KDF combiner (Section 4.3) in isolation.
   They demonstrate that encode() is injective over (protocol_id, label,
   ikm, info, L): distinct inputs, including inputs whose octets
   concatenate to the same string under a different grouping, produce
   distinct framed inputs and therefore distinct outputs.  The full set
   of 38 combiner vectors, covering both KDF classes and every SEAL
   label, is published byte-for-byte as raae-v1-combiner-kdf-
   vectors.json in the draft repository.  All blocks below use ikm equal
   to a single 32-octet element of 0xAA.  Hexadecimal values wrap at 16
   octets per line.

   The first pair frames the same five info octets 01 02 03 04 05 two
   ways.  Because encode length-prefixes each element, the two
   expand_info values differ, so the outputs differ.  The second pair
   shows that the empty sequence and a one-element sequence whose
   element is the empty octet string are distinct.  The third pair
   repeats the distinction in the one-step form, where encode(...ikm)
   and encode(...info) are each a single nested element of the message.

  KDF.29  HKDF-SHA-256, label "commit"  (info = [010203, 0405])
    expand_info = encode(protocol_id, "commit", ...info,
                       I2OSP(32, 2)):
      000a5345414c2d52572d76310006636f
      6d6d6974000301020300020405000200
      20
    output (32 octets):
      0dfb8948fcc220f61f43f291648903b7
      1fe6e5208647b6e18d3308f59fca0fa0

  KDF.30  HKDF-SHA-256, label "commit"  (info = [0102, 030405])
    expand_info = encode(protocol_id, "commit", ...info,
                       I2OSP(32, 2)):
      000a5345414c2d52572d76310006636f
      6d6d6974000201020003030405000200
      20
    output (32 octets):
      299aa40869ae880bc8a064bb5afe38c4
      13f420ff30bcbfac7651d5e248b3db98

  KDF.8  HKDF-SHA-256, label "commit"  (info = [], the empty sequence)
    expand_info = encode(protocol_id, "commit", ...info,
                       I2OSP(32, 2)):
      000a5345414c2d52572d76310006636f
      6d6d697400020020
    output (32 octets):
      90073e3e9f1c855c2b7460e851f75d1a

Sullivan                 Expires 14 January 2027              [Page 127]
Internet-Draft                    raAE                         July 2026

      063a1daf007f81e4a695da1a0f97fca6

  KDF.28  HKDF-SHA-256, label "commit"  (info = [""], one empty element)
    expand_info = encode(protocol_id, "commit", ...info,
                       I2OSP(32, 2)):
      000a5345414c2d52572d76310006636f
      6d6d6974000000020020
    output (32 octets):
      5a4b4b2d59f5989c598f05a0a448acda
      faf27e10914c3894430ee7482c9a1913

  KDF.33  TurboSHAKE-256, label "commit"  (info = [010203, 0405])
    encoded_input = encode(protocol_id, "commit",
                         encode(...ikm), encode(...info),
                         I2OSP(64, 2)):
      000a5345414c2d52572d76310006636f
      6d6d697400220020aaaaaaaaaaaaaaaa
      aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
      aaaaaaaaaaaaaaaa0009000301020300
      02040500020040
    output (64 octets):
      dcefbcdced8b413e18303d2ffe1cac63
      44ee71b13324caa91d1712efc6b81ca8
      9ce2b62ce3aedde0ed16e14d7d17e2f2
      bc69f5e856eb96f9e4845f8522b0a9b5

  KDF.34  TurboSHAKE-256, label "commit"  (info = [0102, 030405])
    encoded_input = encode(protocol_id, "commit",
                         encode(...ikm), encode(...info),
                         I2OSP(64, 2)):
      000a5345414c2d52572d76310006636f
      6d6d697400220020aaaaaaaaaaaaaaaa
      aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
      aaaaaaaaaaaaaaaa0009000201020003
      03040500020040
    output (64 octets):
      02958e6256193c71b0cc4b8ac3273b31
      15d1ac30fd9aab537ad6916206be9828
      9146b56526054b3c11e22f2c375d0b24
      43e8d2ad2e6a89b874aa9ef4ce2c9c77

F.4.  Single Segment, AES-256-GCM, HKDF-SHA-256, 65536

Sullivan                 Expires 14 January 2027              [Page 128]
Internet-Draft                    raAE                         July 2026

   Parameter set:
     cipher suite:
       aead_id       0x0002  (AEAD_AES_256_GCM)
       kdf_id        0x0001  (HKDF-SHA-256)
     geometry:
       segment_max   65536
     operational:
       epoch_length  1
       nonce_mode    random
       snap_id       0x0001  (masked multiset hash)

   payload_info (the KDF frames each element):
     aead_id         ( 2 octets): 0002
     segment_max_be  ( 4 octets): 00010000
     kdf_id          ( 2 octets): 0001
     snap_id         ( 2 octets): 0001
     nonce_mode      ( 1 octets): 00
     epoch_length_u8 ( 1 octets): 01
     salt            (32 octets):
       04040404040404040404040404040404
       04040404040404040404040404040404

   Payload schedule:
     commitment   (32 octets):
       9285553e10209c27bb5858b621426513
       b0832f26d7ee813d9dd62c218ce6972a
     payload_key  (32 octets):
       bb78da70d5e99d36c78e8a8b1a79b620
       e4a4250dd6b471024c379917dfbb2de7
     snap_key     (32 octets):
       953950ab75bdefd67ef15bbd7665b8af
       d3c9ced50ce7cb369e789606fc455025

   Segment 0 (is_final=1):
     nonce        (12 octets):
       030303030303030303030303
     segment_aad  (24 octets):
       00095345414c2d444154410008000000
       0000000000000101
     ciphertext   (12 octets):
       1815f12b13f7ee2532f0fcca
     tag          (16 octets):
       df4b1428af3c5ecb6d804159fec249e0
     contrib      (32 octets):
       73e62d2574a38dc44b406a0c2f2d57b1
       2b7ca777b053cdbb4e9d6f1b3257991a

   accumulator  (32 octets):

Sullivan                 Expires 14 January 2027              [Page 129]
Internet-Draft                    raAE                         July 2026

       73e62d2574a38dc44b406a0c2f2d57b1
       2b7ca777b053cdbb4e9d6f1b3257991a
   mask         (32 octets):
       dfd59806ded61dbf83bab8a7e143da0e
       01714d207c2f53af86e00c875590093b
   wrapped_acc  (32 octets):
       ac33b523aa75907bc8fad2abce6e8dbf
       2a0dea57cc7c9e14c87d639c67c79021
   snapshot_tag (32 octets):
       5a7713eab7ce2f7f246647aa407e14fa
       f295a04333f06c27cdc1193252a9b8bc

F.5.  Single Segment, ChaCha20-Poly1305, HKDF-SHA-256, 16384

   Parameter set:
     cipher suite:
       aead_id       0x001d  (AEAD_CHACHA20_POLY1305)
       kdf_id        0x0001  (HKDF-SHA-256)
     geometry:
       segment_max   16384
     operational:
       epoch_length  1
       nonce_mode    random
       snap_id       0x0001  (masked multiset hash)

   payload_info (the KDF frames each element):
     aead_id         ( 2 octets): 001d
     segment_max_be  ( 4 octets): 00004000
     kdf_id          ( 2 octets): 0001
     snap_id         ( 2 octets): 0001
     nonce_mode      ( 1 octets): 00
     epoch_length_u8 ( 1 octets): 01
     salt            (32 octets):
       04040404040404040404040404040404
       04040404040404040404040404040404

   Payload schedule:
     commitment   (32 octets):
       9ef7166bbce42787fd834f79d29f85b6
       6a050b24f372ecfb79a66b3f2fdc1acb
     payload_key  (32 octets):
       d0f1d392a371642db684a23858c0193c
       2d7406cb4360c81ef9190391cacf885f
     snap_key     (32 octets):
       b156708dc559791d78014bae5e01b5fd
       f8a397c2d140fd9b9468e3cceeb8aa5d

   Segment 0 (is_final=1):

Sullivan                 Expires 14 January 2027              [Page 130]
Internet-Draft                    raAE                         July 2026

     nonce        (12 octets):
       030303030303030303030303
     segment_aad  (24 octets):
       00095345414c2d444154410008000000
       0000000000000101
     ciphertext   (12 octets):
       f1d968bc047a7bf6a15ef400
     tag          (16 octets):
       567deca2d91b732fd1f814a65335df79
     contrib      (32 octets):
       235bfbd70b8ce751d3720bea8351f039
       dac89c0055d817a5b949a8582c590035

   accumulator  (32 octets):
       235bfbd70b8ce751d3720bea8351f039
       dac89c0055d817a5b949a8582c590035
   mask         (32 octets):
       3beeed96dd10cd4bc3ec5f439a789d84
       acd1542b0edabd0c48c1bfb4766af53e
   wrapped_acc  (32 octets):
       18b51641d69c2a1a109e54a919296dbd
       7619c82b5b02aaa9f18817ec5a33f50b
   snapshot_tag (32 octets):
       b7a4b41dfb8de76d9ebaf0833f72d03b
       277bab9453c9085553dee456c998f4b7

F.6.  Single Segment, ChaCha20-Poly1305, HKDF-SHA-256, 65536

   Parameter set:
     cipher suite:
       aead_id       0x001d  (AEAD_CHACHA20_POLY1305)
       kdf_id        0x0001  (HKDF-SHA-256)
     geometry:
       segment_max   65536
     operational:
       epoch_length  1
       nonce_mode    random
       snap_id       0x0001  (masked multiset hash)

   payload_info (the KDF frames each element):
     aead_id         ( 2 octets): 001d
     segment_max_be  ( 4 octets): 00010000
     kdf_id          ( 2 octets): 0001
     snap_id         ( 2 octets): 0001
     nonce_mode      ( 1 octets): 00
     epoch_length_u8 ( 1 octets): 01
     salt            (32 octets):
       04040404040404040404040404040404

Sullivan                 Expires 14 January 2027              [Page 131]
Internet-Draft                    raAE                         July 2026

       04040404040404040404040404040404

   Payload schedule:
     commitment   (32 octets):
       ed76666a233fc9724c82f209aea191fa
       bdf8e65f12fa97f0a4e317839ee56f19
     payload_key  (32 octets):
       a859c3a684d35378bbbcf7ed48286313
       3e3af8d2cbf8d40687d693243c32cdea
     snap_key     (32 octets):
       aa42a852946818754780e48a9209a451
       345367bd07a04ad794c62a703366aa90

   Segment 0 (is_final=1):
     nonce        (12 octets):
       030303030303030303030303
     segment_aad  (24 octets):
       00095345414c2d444154410008000000
       0000000000000101
     ciphertext   (12 octets):
       db9cf72ba226e3210aa9fcb5
     tag          (16 octets):
       f5048c0d08372770fe066f2b5c052ab8
     contrib      (32 octets):
       aec6ba40c8e7234e704d3a689866dcb0
       efb84a6f0c75e27ea45756486d2b9a72

   accumulator  (32 octets):
       aec6ba40c8e7234e704d3a689866dcb0
       efb84a6f0c75e27ea45756486d2b9a72
   mask         (32 octets):
       b372584c8a52e2daec61e6b3b2740b87
       c56913270ebf83456636645bccbd125c
   wrapped_acc  (32 octets):
       1db4e20c42b5c1949c2cdcdb2a12d737
       2ad1594802ca613bc2613213a196882e
   snapshot_tag (32 octets):
       2eb1e1d010fa697a4c577445e36aacc6
       3cfbaafc893b8cd6730099c560d3524a

F.7.  Single Segment, AEGIS-256, HKDF-SHA-256, 16384

   Parameter set:
     cipher suite:
       aead_id       0x0021  (AEAD_AEGIS256)
       kdf_id        0x0001  (HKDF-SHA-256)
     geometry:
       segment_max   16384

Sullivan                 Expires 14 January 2027              [Page 132]
Internet-Draft                    raAE                         July 2026

     operational:
       epoch_length  63
       nonce_mode    random
       snap_id       0x0001  (masked multiset hash)

   payload_info (the KDF frames each element):
     aead_id         ( 2 octets): 0021
     segment_max_be  ( 4 octets): 00004000
     kdf_id          ( 2 octets): 0001
     snap_id         ( 2 octets): 0001
     nonce_mode      ( 1 octets): 00
     epoch_length_u8 ( 1 octets): 3f
     salt            (32 octets):
       04040404040404040404040404040404
       04040404040404040404040404040404

   Payload schedule:
     commitment   (32 octets):
       c4e0d853da06a0d7da0f062952ce8c1d
       c9936ec06b883accd2117aed9475f0cb
     payload_key  (32 octets):
       7b69e4c6a70d806c97315c4f37e698f8
       ad104677c20b4336ad81c9de7544246a
     snap_key     (32 octets):
       fb987468cf2e7f1321b8130b68933c7f
       039a39b0606fc7bc106d3169a9323d73

   Segment 0 (is_final=1):
     nonce        (32 octets):
       05050505050505050505050505050505
       05050505050505050505050505050505
     segment_aad  (24 octets):
       00095345414c2d444154410008000000
       0000000000000101
     ciphertext   (12 octets):
       91c6c2100dd9d365e5a6df47
     tag          (16 octets):
       f48dd988d409eb7b90b4fe6447ec7b25
     contrib      (32 octets):
       176e02c54d994c99f50928a7c67386fb
       ea17424dbcf70230e9f4bea73f5265a4

   accumulator  (32 octets):
       176e02c54d994c99f50928a7c67386fb
       ea17424dbcf70230e9f4bea73f5265a4
   mask         (32 octets):
       5e9bb90f0671afe58fb72584dce62ae6
       6bfaa33159a97eefc2de2304c8b2528a

Sullivan                 Expires 14 January 2027              [Page 133]
Internet-Draft                    raAE                         July 2026

   wrapped_acc  (32 octets):
       49f5bbca4be8e37c7abe0d231a95ac1d
       81ede17ce55e7cdf2b2a9da3f7e0372e
   snapshot_tag (32 octets):
       cb35b42e68a265b2c6a09de9b381044b
       29f46e98342a44f6eaae3eb8f7470789

F.8.  Single Segment, AEGIS-256, HKDF-SHA-256, 65536

   Parameter set:
     cipher suite:
       aead_id       0x0021  (AEAD_AEGIS256)
       kdf_id        0x0001  (HKDF-SHA-256)
     geometry:
       segment_max   65536
     operational:
       epoch_length  63
       nonce_mode    random
       snap_id       0x0001  (masked multiset hash)

   payload_info (the KDF frames each element):
     aead_id         ( 2 octets): 0021
     segment_max_be  ( 4 octets): 00010000
     kdf_id          ( 2 octets): 0001
     snap_id         ( 2 octets): 0001
     nonce_mode      ( 1 octets): 00
     epoch_length_u8 ( 1 octets): 3f
     salt            (32 octets):
       04040404040404040404040404040404
       04040404040404040404040404040404

   Payload schedule:
     commitment   (32 octets):
       6bb3b3000bbdba28de3a8fcf29fd862e
       094e2e28c6df0d677aeba07ab747fe18
     payload_key  (32 octets):
       425c1db85e1d3c85025ab41d5e263db9
       c4969b4599942fa582d2394f0c5870e4
     snap_key     (32 octets):
       4bb7fb2a1e5036cbe9aa018af5b5fe56
       2fd60a1c388c54168621a40530ae4237

   Segment 0 (is_final=1):
     nonce        (32 octets):
       05050505050505050505050505050505
       05050505050505050505050505050505
     segment_aad  (24 octets):
       00095345414c2d444154410008000000

Sullivan                 Expires 14 January 2027              [Page 134]
Internet-Draft                    raAE                         July 2026

       0000000000000101
     ciphertext   (12 octets):
       f53c634ccf8aab1157fa44fd
     tag          (16 octets):
       90b6fa24f3cd9cb2b18574536f7bfdfc
     contrib      (32 octets):
       0b649e2b2702281a67ac5ab72c4b8e68
       afbe4135ee9a5fe693f34b6383377a24

   accumulator  (32 octets):
       0b649e2b2702281a67ac5ab72c4b8e68
       afbe4135ee9a5fe693f34b6383377a24
   mask         (32 octets):
       462e3c1a58d6f5f665988d3a28769729
       3bb3fa914b43a3d99b69da1b9491d1a3
   wrapped_acc  (32 octets):
       4d4aa2317fd4ddec0234d78d043d1941
       940dbba4a5d9fc3f089a917817a6ab87
   snapshot_tag (32 octets):
       34303daf7ba4f37eeddd4fd2bb382bb2
       439ceaecb5c240fc2839e2602d3a7d33

F.9.  Two Segment, AES-256-GCM, HKDF-SHA-256, 16384

   Parameter set:
     cipher suite:
       aead_id       0x0002  (AEAD_AES_256_GCM)
       kdf_id        0x0001  (HKDF-SHA-256)
     geometry:
       segment_max   16384
     operational:
       epoch_length  1
       nonce_mode    random
       snap_id       0x0001  (masked multiset hash)

   payload_info (the KDF frames each element):
     aead_id         ( 2 octets): 0002
     segment_max_be  ( 4 octets): 00004000
     kdf_id          ( 2 octets): 0001
     snap_id         ( 2 octets): 0001
     nonce_mode      ( 1 octets): 00
     epoch_length_u8 ( 1 octets): 01
     salt            (32 octets):
       04040404040404040404040404040404
       04040404040404040404040404040404

   Payload schedule:
     commitment   (32 octets):

Sullivan                 Expires 14 January 2027              [Page 135]
Internet-Draft                    raAE                         July 2026

       47ea0ec7409b9b95d676019917a19f1c
       5831eb236aba459063458e525d130d0c
     payload_key  (32 octets):
       c1f2663e99977428dc0fec1566ce15e9
       1398634ab9b1d004945de48560707062
     snap_key     (32 octets):
       bc314ecaa8ff6c1c4ebc13b54597a10d
       2bcf412b40c428a0e411a828fcfb52ef

   Segment 0 (is_final=0):
     nonce        (12 octets):
       030303030303030303030303
     segment_aad  (24 octets):
       00095345414c2d444154410008000000
       0000000000000100
     ciphertext   (12 octets):
       6a2b84e72ce8edbf4259eebd
     tag          (16 octets):
       1574936244d54aedf589c87002dbac90
     contrib      (32 octets):
       322c4622cd9f50552740d1eee7a530eb
       3fd371e44178b8ce18816b53dd3f1587
   Segment 1 (is_final=1):
     nonce        (12 octets):
       070707070707070707070707
     segment_aad  (24 octets):
       00095345414c2d444154410008000000
       0000000001000101
     ciphertext   (20 octets):
       50b0eda5d7a86a5a94e501f317020173
       6d755d56
     tag          (16 octets):
       f22fb2b4bfed679d1d90d2a0814e429c
     contrib      (32 octets):
       7455e0472187dcd04a5747d508587a8c
       9f3e281818188be0edd46be8e32d4a6e

   accumulator  (32 octets):
       4679a665ec188c856d17963beffd4a67
       a0ed59fc5960332ef55500bb3e125fe9
   mask         (32 octets):
       ad55f19bb8997c8ad0def92830f0ce09
       4542ec804f7b99e66f294164985979a8
   wrapped_acc  (32 octets):
       eb2c57fe5481f00fbdc96f13df0d846e
       e5afb57c161baac89a7c41dfa64b2641
   snapshot_tag (32 octets):
       a08c73b5c414542cc06830d893d0eaca

Sullivan                 Expires 14 January 2027              [Page 136]
Internet-Draft                    raAE                         July 2026

       c749418dea32b11d5cc121d6b2db93b8

F.10.  Two Segment, AES-256-GCM, HKDF-SHA-256, 65536

   Parameter set:
     cipher suite:
       aead_id       0x0002  (AEAD_AES_256_GCM)
       kdf_id        0x0001  (HKDF-SHA-256)
     geometry:
       segment_max   65536
     operational:
       epoch_length  1
       nonce_mode    random
       snap_id       0x0001  (masked multiset hash)

   payload_info (the KDF frames each element):
     aead_id         ( 2 octets): 0002
     segment_max_be  ( 4 octets): 00010000
     kdf_id          ( 2 octets): 0001
     snap_id         ( 2 octets): 0001
     nonce_mode      ( 1 octets): 00
     epoch_length_u8 ( 1 octets): 01
     salt            (32 octets):
       04040404040404040404040404040404
       04040404040404040404040404040404

   Payload schedule:
     commitment   (32 octets):
       9285553e10209c27bb5858b621426513
       b0832f26d7ee813d9dd62c218ce6972a
     payload_key  (32 octets):
       bb78da70d5e99d36c78e8a8b1a79b620
       e4a4250dd6b471024c379917dfbb2de7
     snap_key     (32 octets):
       953950ab75bdefd67ef15bbd7665b8af
       d3c9ced50ce7cb369e789606fc455025

   Segment 0 (is_final=0):
     nonce        (12 octets):
       030303030303030303030303
     segment_aad  (24 octets):
       00095345414c2d444154410008000000
       0000000000000100
     ciphertext   (12 octets):
       1815f12b13f7ee2532f0fcca
     tag          (16 octets):
       2597e2f2243b98c4bb7f320dc2f46ce3
     contrib      (32 octets):

Sullivan                 Expires 14 January 2027              [Page 137]
Internet-Draft                    raAE                         July 2026

       0d8b3cb23192377e88232945f623150b
       1a1c0b61745a4fa39a5f65e162b6e672
   Segment 1 (is_final=1):
     nonce        (12 octets):
       070707070707070707070707
     segment_aad  (24 octets):
       00095345414c2d444154410008000000
       0000000001000101
     ciphertext   (20 octets):
       f17c9cac3693dcb4bdd524714da804d4
       c4390056
     tag          (16 octets):
       97d4f0d1303a2b112eac5aae081ef6bf
     contrib      (32 octets):
       a8e8df627852127e058558353da2def9
       9a83928b4c96ef37a5d0b08068b1a87c

   accumulator  (32 octets):
       a563e3d049c025008da67170cb81cbf2
       809f99ea38cca0943f8fd5610a074e0e
   mask         (32 octets):
       946a2744cf3fd572a03993d434705704
       47d1ce03f4769035a5058780660e5eff
   wrapped_acc  (32 octets):
       3109c49486fff0722d9fe2a4fff19cf6
       c74e57e9ccba30a19a8a52e16c0910f1
   snapshot_tag (32 octets):
       5ce50c9e90db4bbc28297372e401625c
       2e43203ce8008c452ea4355f0941ef67

F.11.  Two Segment, ChaCha20-Poly1305, HKDF-SHA-256, 16384

   Parameter set:
     cipher suite:
       aead_id       0x001d  (AEAD_CHACHA20_POLY1305)
       kdf_id        0x0001  (HKDF-SHA-256)
     geometry:
       segment_max   16384
     operational:
       epoch_length  1
       nonce_mode    random
       snap_id       0x0001  (masked multiset hash)

   payload_info (the KDF frames each element):
     aead_id         ( 2 octets): 001d
     segment_max_be  ( 4 octets): 00004000
     kdf_id          ( 2 octets): 0001
     snap_id         ( 2 octets): 0001

Sullivan                 Expires 14 January 2027              [Page 138]
Internet-Draft                    raAE                         July 2026

     nonce_mode      ( 1 octets): 00
     epoch_length_u8 ( 1 octets): 01
     salt            (32 octets):
       04040404040404040404040404040404
       04040404040404040404040404040404

   Payload schedule:
     commitment   (32 octets):
       9ef7166bbce42787fd834f79d29f85b6
       6a050b24f372ecfb79a66b3f2fdc1acb
     payload_key  (32 octets):
       d0f1d392a371642db684a23858c0193c
       2d7406cb4360c81ef9190391cacf885f
     snap_key     (32 octets):
       b156708dc559791d78014bae5e01b5fd
       f8a397c2d140fd9b9468e3cceeb8aa5d

   Segment 0 (is_final=0):
     nonce        (12 octets):
       030303030303030303030303
     segment_aad  (24 octets):
       00095345414c2d444154410008000000
       0000000000000100
     ciphertext   (12 octets):
       f1d968bc047a7bf6a15ef400
     tag          (16 octets):
       0359d2adbe709e374e6fae7830001295
     contrib      (32 octets):
       92c78ada192f60346b35491986711454
       9e20d72ede4b78a9567cf414365bc781
   Segment 1 (is_final=1):
     nonce        (12 octets):
       070707070707070707070707
     segment_aad  (24 octets):
       00095345414c2d444154410008000000
       0000000001000101
     ciphertext   (20 octets):
       4d8f7a1b6aa589808c384a52bd90e4d0
       a44ed35e
     tag          (16 octets):
       61c3caca0ecda929269f52978f4a60e1
     contrib      (32 octets):
       85db0789dfd84dfaeb095fc28101d929
       af58800d6ae8882353c83c9a6013492c

   accumulator  (32 octets):
       171c8d53c6f72dce803c16db0770cd7d
       31785723b4a3f08a05b4c88e56488ead

Sullivan                 Expires 14 January 2027              [Page 139]
Internet-Draft                    raAE                         July 2026

   mask         (32 octets):
       e399bc51986eaf7f8d7cbcb3e76da43a
       b9607c01e413dd462639c4fd50191f32
   wrapped_acc  (32 octets):
       f48531025e9982b10d40aa68e01d6947
       88182b2250b02dcc238d0c730651919f
   snapshot_tag (32 octets):
       f4bfbdff53178451463ef23c73a31ab5
       06a6acfb49282af14598b2f49f32ceeb

F.12.  Two Segment, ChaCha20-Poly1305, HKDF-SHA-256, 65536

   Parameter set:
     cipher suite:
       aead_id       0x001d  (AEAD_CHACHA20_POLY1305)
       kdf_id        0x0001  (HKDF-SHA-256)
     geometry:
       segment_max   65536
     operational:
       epoch_length  1
       nonce_mode    random
       snap_id       0x0001  (masked multiset hash)

   payload_info (the KDF frames each element):
     aead_id         ( 2 octets): 001d
     segment_max_be  ( 4 octets): 00010000
     kdf_id          ( 2 octets): 0001
     snap_id         ( 2 octets): 0001
     nonce_mode      ( 1 octets): 00
     epoch_length_u8 ( 1 octets): 01
     salt            (32 octets):
       04040404040404040404040404040404
       04040404040404040404040404040404

   Payload schedule:
     commitment   (32 octets):
       ed76666a233fc9724c82f209aea191fa
       bdf8e65f12fa97f0a4e317839ee56f19
     payload_key  (32 octets):
       a859c3a684d35378bbbcf7ed48286313
       3e3af8d2cbf8d40687d693243c32cdea
     snap_key     (32 octets):
       aa42a852946818754780e48a9209a451
       345367bd07a04ad794c62a703366aa90

   Segment 0 (is_final=0):
     nonce        (12 octets):
       030303030303030303030303

Sullivan                 Expires 14 January 2027              [Page 140]
Internet-Draft                    raAE                         July 2026

     segment_aad  (24 octets):
       00095345414c2d444154410008000000
       0000000000000100
     ciphertext   (12 octets):
       db9cf72ba226e3210aa9fcb5
     tag          (16 octets):
       6423011bc81f13a30ef51ff32f209cff
     contrib      (32 octets):
       fab6f671b544bb356337a2b811475663
       cc5fab22c29a6770c6cd035bc44d0554
   Segment 1 (is_final=1):
     nonce        (12 octets):
       070707070707070707070707
     segment_aad  (24 octets):
       00095345414c2d444154410008000000
       0000000001000101
     ciphertext   (20 octets):
       85ead0c3a974dd014622ec0e2ba9a6e6
       199a7b88
     tag          (16 octets):
       3ee3399f906110b8e7900805f2b72a85
     contrib      (32 octets):
       361a4ce5127403db2e54950d48a81af1
       a71913771db2885bcb6bcdab971a3611

   accumulator  (32 octets):
       ccacba94a730b8ee4d6337b559ef4c92
       6b46b855df28ef2b0da6cef053573345
   mask         (32 octets):
       b62dbfa20e0725e6cc0bb81b169f6294
       a8b1d7b94968ac589c9b7ebcffc37882
   wrapped_acc  (32 octets):
       7a810536a9379d0881688fae4f702e06
       c3f76fec96404373913db04cac944bc7
   snapshot_tag (32 octets):
       e0b3b1cadba56bb4994d7872b57aff7f
       42215a8e2b7a2241cffd5107f571d560

F.13.  Two Segment, AEGIS-256, HKDF-SHA-256, 16384

   Parameter set:
     cipher suite:
       aead_id       0x0021  (AEAD_AEGIS256)
       kdf_id        0x0001  (HKDF-SHA-256)
     geometry:
       segment_max   16384
     operational:
       epoch_length  63

Sullivan                 Expires 14 January 2027              [Page 141]
Internet-Draft                    raAE                         July 2026

       nonce_mode    random
       snap_id       0x0001  (masked multiset hash)

   payload_info (the KDF frames each element):
     aead_id         ( 2 octets): 0021
     segment_max_be  ( 4 octets): 00004000
     kdf_id          ( 2 octets): 0001
     snap_id         ( 2 octets): 0001
     nonce_mode      ( 1 octets): 00
     epoch_length_u8 ( 1 octets): 3f
     salt            (32 octets):
       04040404040404040404040404040404
       04040404040404040404040404040404

   Payload schedule:
     commitment   (32 octets):
       c4e0d853da06a0d7da0f062952ce8c1d
       c9936ec06b883accd2117aed9475f0cb
     payload_key  (32 octets):
       7b69e4c6a70d806c97315c4f37e698f8
       ad104677c20b4336ad81c9de7544246a
     snap_key     (32 octets):
       fb987468cf2e7f1321b8130b68933c7f
       039a39b0606fc7bc106d3169a9323d73

   Segment 0 (is_final=0):
     nonce        (32 octets):
       05050505050505050505050505050505
       05050505050505050505050505050505
     segment_aad  (24 octets):
       00095345414c2d444154410008000000
       0000000000000100
     ciphertext   (12 octets):
       91c6c2100dd9d365e5a6df47
     tag          (16 octets):
       c4837c14751196e622301d3e8a5e00b2
     contrib      (32 octets):
       002805d6180149a3852b8afd82b40db9
       4d4e2af23a11bfb1e2922e5e3efb4210
   Segment 1 (is_final=1):
     nonce        (32 octets):
       09090909090909090909090909090909
       09090909090909090909090909090909
     segment_aad  (24 octets):
       00095345414c2d444154410008000000
       0000000001000101
     ciphertext   (20 octets):
       006704b222fa9285da01b706be831b76

Sullivan                 Expires 14 January 2027              [Page 142]
Internet-Draft                    raAE                         July 2026

       ead7332c
     tag          (16 octets):
       9965b57015a2b058514fb1fca7708242
     contrib      (32 octets):
       f1edcc4f336fb96b7319d30bca9a9194
       e6795e34a3a0b19652521d749dd2e767

   accumulator  (32 octets):
       f1c5c9992b6ef0c8f63259f6482e9c2d
       ab3774c699b10e27b0c0332aa329a577
   mask         (32 octets):
       0affb88aa98af47cf91aa6c80b733679
       070bc7b44df36f22859f0c89a91a3cc9
   wrapped_acc  (32 octets):
       fb3a711382e404b40f28ff3e435daa54
       ac3cb372d4426105355f3fa30a3399be
   snapshot_tag (32 octets):
       3d8899806c647a198c1c4e23f6ce8a2e
       be5d8cba11d65a16bbd2e7d5c4f415fc

F.14.  Two Segment, AEGIS-256, HKDF-SHA-256, 65536

   Parameter set:
     cipher suite:
       aead_id       0x0021  (AEAD_AEGIS256)
       kdf_id        0x0001  (HKDF-SHA-256)
     geometry:
       segment_max   65536
     operational:
       epoch_length  63
       nonce_mode    random
       snap_id       0x0001  (masked multiset hash)

   payload_info (the KDF frames each element):
     aead_id         ( 2 octets): 0021
     segment_max_be  ( 4 octets): 00010000
     kdf_id          ( 2 octets): 0001
     snap_id         ( 2 octets): 0001
     nonce_mode      ( 1 octets): 00
     epoch_length_u8 ( 1 octets): 3f
     salt            (32 octets):
       04040404040404040404040404040404
       04040404040404040404040404040404

   Payload schedule:
     commitment   (32 octets):
       6bb3b3000bbdba28de3a8fcf29fd862e
       094e2e28c6df0d677aeba07ab747fe18

Sullivan                 Expires 14 January 2027              [Page 143]
Internet-Draft                    raAE                         July 2026

     payload_key  (32 octets):
       425c1db85e1d3c85025ab41d5e263db9
       c4969b4599942fa582d2394f0c5870e4
     snap_key     (32 octets):
       4bb7fb2a1e5036cbe9aa018af5b5fe56
       2fd60a1c388c54168621a40530ae4237

   Segment 0 (is_final=0):
     nonce        (32 octets):
       05050505050505050505050505050505
       05050505050505050505050505050505
     segment_aad  (24 octets):
       00095345414c2d444154410008000000
       0000000000000100
     ciphertext   (12 octets):
       f53c634ccf8aab1157fa44fd
     tag          (16 octets):
       c466896ca6da6509959b1c0c10dd688d
     contrib      (32 octets):
       e558127502262f71ac5ce07b9e0e101f
       177a5428a10ecb464cfcd731d75b35fe
   Segment 1 (is_final=1):
     nonce        (32 octets):
       09090909090909090909090909090909
       09090909090909090909090909090909
     segment_aad  (24 octets):
       00095345414c2d444154410008000000
       0000000001000101
     ciphertext   (20 octets):
       40865396160e5aa905a96d3a6552f6cc
       fecf30ec
     tag          (16 octets):
       ddd6e4979a6d648400b236abc617d033
     contrib      (32 octets):
       23f4cfab2a5601a75d7f87148c5f32fd
       d8ab5ac1a16d25c9688e61b7d09171bb

   accumulator  (32 octets):
       c6acddde28702ed6f123676f125122e2
       cfd10ee90063ee8f2472b68607ca4445
   mask         (32 octets):
       8d8832a7c865352b6ebcfabaa1c85c69
       0aacfbfb2bf08a73cc090281bea1ca42
   wrapped_acc  (32 octets):
       4b24ef79e0151bfd9f9f9dd5b3997e8b
       c57df5122b9364fce87bb407b96b8e07
   snapshot_tag (32 octets):
       016300d12db61dc1b6b11fbeee8ecaf8

Sullivan                 Expires 14 January 2027              [Page 144]
Internet-Draft                    raAE                         July 2026

       614efe5802f3fc919ab8aa665cdf9294

F.15.  TurboSHAKE-256 Cipher Suite Vectors

   These two vectors exercise the one-step TurboSHAKE-256 cipher suite
   (Table 11), for which Nh = 64.  The commitment, snap_key, contrib,
   the accumulator, and the snapshot tag are therefore 64 octets, while
   payload_key (Nk) and the nonces (Nn) are unchanged.

F.15.1.  Single Segment, AES-256-GCM, TurboSHAKE-256, 65536

   Parameter set:
     cipher suite:
       aead_id       0x0002  (AEAD_AES_256_GCM)
       kdf_id        0x0013  (TurboSHAKE-256)
     geometry:
       segment_max   65536
     operational:
       epoch_length  1
       nonce_mode    random
       snap_id       0x0001  (masked multiset hash)

   payload_info (the KDF frames each element):
     aead_id         ( 2 octets): 0002
     segment_max_be  ( 4 octets): 00010000
     kdf_id          ( 2 octets): 0013
     snap_id         ( 2 octets): 0001
     nonce_mode      ( 1 octets): 00
     epoch_length_u8 ( 1 octets): 01
     salt            (32 octets):
       04040404040404040404040404040404
       04040404040404040404040404040404

   Payload schedule:
     commitment   (64 octets):
       b19991b71ed275d98070eab735179d60
       be397354a85f6d6f58e74dcb90f0ff43
       8271da594a267d81aaa74a88736ba549
       d9e88c1b9d9a972135220b76c9568483
     payload_key  (32 octets):
       bdecddf1340029c520b9b4a9e1b15144
       d283209261a58113294728e337d14ea8
     snap_key     (64 octets):
       92fc2e47ac72bbdbcac62a67ced07dad
       a1e907bd82e92a68ba5f6098ec067931
       e59683904d5213ddd0abe237ac0f9450
       ef33180028f2ea7e47d738e6f3faed01

Sullivan                 Expires 14 January 2027              [Page 145]
Internet-Draft                    raAE                         July 2026

   Segment 0 (is_final=1):
     nonce        (12 octets):
       030303030303030303030303
     segment_aad  (24 octets):
       00095345414c2d444154410008000000
       0000000000000101
     ciphertext   (12 octets):
       429b98993decaa7c1792d52d
     tag          (16 octets):
       be8cd796db427f283859fa6722708d5b
     contrib      (64 octets):
       371669ebb5f158b73bd65f0f66a33878
       3e2372d1cbfced76ff866e264fb6c85f
       151dcdf2662d286cc065844a2b5aff57
       200d689ad4ee7d027df20be43747bbf2

   accumulator  (64 octets):
       371669ebb5f158b73bd65f0f66a33878
       3e2372d1cbfced76ff866e264fb6c85f
       151dcdf2662d286cc065844a2b5aff57
       200d689ad4ee7d027df20be43747bbf2
   mask         (64 octets):
       39bc62cc8b75a8745da4cf36061350a1
       a1c439359cde43df5355ce7c8527cb0f
       23dbf74cd0534517d729fc1ebad29d15
       b27c8f0373ca74ccc7a19de9e174ce39
   wrapped_acc  (64 octets):
       0eaa0b273e84f0c36672903960b068d9
       9fe74be45722aea9acd3a05aca910350
       36c63abeb67e6d7b174c785491886242
       9271e799a72409ceba53960dd63375cb
   snapshot_tag (64 octets):
       75dd576b0a3f5ba7181b3e183e6ef741
       b1e7ea93a7852fc0c7657ed905f74eb6
       45570ac0b639c2f901797f1152c8c7d4
       196aed0cf4af2308431af242399b0c22

F.15.2.  Two Segment, AES-256-GCM, TurboSHAKE-256, 65536

   Parameter set:
     cipher suite:
       aead_id       0x0002  (AEAD_AES_256_GCM)
       kdf_id        0x0013  (TurboSHAKE-256)
     geometry:
       segment_max   65536
     operational:
       epoch_length  1
       nonce_mode    random

Sullivan                 Expires 14 January 2027              [Page 146]
Internet-Draft                    raAE                         July 2026

       snap_id       0x0001  (masked multiset hash)

   payload_info (the KDF frames each element):
     aead_id         ( 2 octets): 0002
     segment_max_be  ( 4 octets): 00010000
     kdf_id          ( 2 octets): 0013
     snap_id         ( 2 octets): 0001
     nonce_mode      ( 1 octets): 00
     epoch_length_u8 ( 1 octets): 01
     salt            (32 octets):
       04040404040404040404040404040404
       04040404040404040404040404040404

   Payload schedule:
     commitment   (64 octets):
       b19991b71ed275d98070eab735179d60
       be397354a85f6d6f58e74dcb90f0ff43
       8271da594a267d81aaa74a88736ba549
       d9e88c1b9d9a972135220b76c9568483
     payload_key  (32 octets):
       bdecddf1340029c520b9b4a9e1b15144
       d283209261a58113294728e337d14ea8
     snap_key     (64 octets):
       92fc2e47ac72bbdbcac62a67ced07dad
       a1e907bd82e92a68ba5f6098ec067931
       e59683904d5213ddd0abe237ac0f9450
       ef33180028f2ea7e47d738e6f3faed01

   Segment 0 (is_final=0):
     nonce        (12 octets):
       030303030303030303030303
     segment_aad  (24 octets):
       00095345414c2d444154410008000000
       0000000000000100
     ciphertext   (12 octets):
       429b98993decaa7c1792d52d
     tag          (16 octets):
       c2afc8ba9d2d163d600a88ed34420cc8
     contrib      (64 octets):
       97b151445a7f50b4f41811e8b9d0f4c9
       04648ba6d13085e7d7180d94321aa3f8
       4f895847053ce9c66e4b27f4251f6ffc
       356e72ec1a867e2b405163732b65b8c1
   Segment 1 (is_final=1):
     nonce        (12 octets):
       070707070707070707070707
     segment_aad  (24 octets):
       00095345414c2d444154410008000000

Sullivan                 Expires 14 January 2027              [Page 147]
Internet-Draft                    raAE                         July 2026

       0000000001000101
     ciphertext   (20 octets):
       7597ac827f175c664e7bebb8b828753b
       fc778463
     tag          (16 octets):
       78545349b2e5092b9d279c97abfe1511
     contrib      (64 octets):
       df8a6116f5535741f7973cfbb2aa6ef0
       78f54da534fea79f953ef95b6a60c5d8
       eaa6a034c9dc8186f27726ae99772694
       0cb46e83581e1466f34a46a76abbca41

   accumulator  (64 octets):
       483b3052af2c07f5038f2d130b7a9a39
       7c91c603e5ce22784226f4cf587a6620
       a52ff873cce068409c3c015abc684968
       39da1c6f42986a4db31b25d441de7280
   mask         (64 octets):
       49cc05dd25532375d143f69b276b0b8d
       5246388217b39b7ef2262b6e7f293fdc
       a43408f13b9172cb3e98a72c735d2546
       e15b860122ebea099a7cbd987bfee991
   wrapped_acc  (64 octets):
       01f7358f8a7f2480d2ccdb882c1191b4
       2ed7fe81f27db906b000dfa1275359fc
       011bf082f7711a8ba2a4a676cf356c2e
       d8819a6e607380442967984c3a209b11
   snapshot_tag (64 octets):
       f6b15b8cf01fd11587f9fe7a32185abe
       31ac84194ae3eed199a5b19ba83b0cdf
       bf0c8e3b4f48199b01bdd1710d87c076
       9d0cdc82f5292a913b7f1d14cd93b843

F.16.  Segment Rewrite Vector

   This vector applies RewriteSegment (Section 4.9.2.1) to segment 0 of
   a two-segment AES-256-GCM message, replacing its plaintext under a
   fresh nonce.  acc_delta = old_contrib XOR new_contrib, the new
   accumulator is the old accumulator XOR acc_delta, and the snapshot
   tag is recomputed over the count and the new accumulator.

F.16.1.  Two Segment, AES-256-GCM, HKDF-SHA-256, 65536 (Rewrite)

Sullivan                 Expires 14 January 2027              [Page 148]
Internet-Draft                    raAE                         July 2026

   Parameter set:
     cipher suite:
       aead_id       0x0002  (AEAD_AES_256_GCM)
       kdf_id        0x0001  (HKDF-SHA-256)
     geometry:
       segment_max   65536
     operational:
       epoch_length  1
       nonce_mode    random
       snap_id       0x0001  (masked multiset hash)

   payload_info (the KDF frames each element):
     aead_id         ( 2 octets): 0002
     segment_max_be  ( 4 octets): 00010000
     kdf_id          ( 2 octets): 0001
     snap_id         ( 2 octets): 0001
     nonce_mode      ( 1 octets): 00
     epoch_length_u8 ( 1 octets): 01
     salt            (32 octets):
       04040404040404040404040404040404
       04040404040404040404040404040404

   Payload schedule:
     commitment   (32 octets):
       9285553e10209c27bb5858b621426513
       b0832f26d7ee813d9dd62c218ce6972a
     payload_key  (32 octets):
       bb78da70d5e99d36c78e8a8b1a79b620
       e4a4250dd6b471024c379917dfbb2de7
     snap_key     (32 octets):
       953950ab75bdefd67ef15bbd7665b8af
       d3c9ced50ce7cb369e789606fc455025

   Segment 0 (is_final=0):
     nonce        (12 octets):
       030303030303030303030303
     segment_aad  (24 octets):
       00095345414c2d444154410008000000
       0000000000000100
     ciphertext   (12 octets):
       1815f12b13f7ee2532f0fcca
     tag          (16 octets):
       2597e2f2243b98c4bb7f320dc2f46ce3
     contrib      (32 octets):
       0d8b3cb23192377e88232945f623150b
       1a1c0b61745a4fa39a5f65e162b6e672
   Segment 1 (is_final=1):
     nonce        (12 octets):

Sullivan                 Expires 14 January 2027              [Page 149]
Internet-Draft                    raAE                         July 2026

       070707070707070707070707
     segment_aad  (24 octets):
       00095345414c2d444154410008000000
       0000000001000101
     ciphertext   (20 octets):
       f17c9cac3693dcb4bdd524714da804d4
       c4390056
     tag          (16 octets):
       97d4f0d1303a2b112eac5aae081ef6bf
     contrib      (32 octets):
       a8e8df627852127e058558353da2def9
       9a83928b4c96ef37a5d0b08068b1a87c

   accumulator  (32 octets):
       a563e3d049c025008da67170cb81cbf2
       809f99ea38cca0943f8fd5610a074e0e
   mask         (32 octets):
       946a2744cf3fd572a03993d434705704
       47d1ce03f4769035a5058780660e5eff
   wrapped_acc  (32 octets):
       3109c49486fff0722d9fe2a4fff19cf6
       c74e57e9ccba30a19a8a52e16c0910f1
   snapshot_tag (32 octets):
       5ce50c9e90db4bbc28297372e401625c
       2e43203ce8008c452ea4355f0941ef67

   Rewrite of segment 0:
     new_nonce    (12 octets):
       0b0b0b0b0b0b0b0b0b0b0b0b
     new_ciphertext (22 octets):
       f20adfd4e5ab8016903d7eaa022f65c7
       0a3f9988b1e5
     new_tag      (16 octets):
       ff3268f44b36f41c84a2e85d0a975d38
     old_contrib  (32 octets):
       0d8b3cb23192377e88232945f623150b
       1a1c0b61745a4fa39a5f65e162b6e672
     new_contrib  (32 octets):
       d1799e445a84608aae7dfca35bc03813
       138fd92f58adb6c1fec48907e9a90f9e
     acc_delta    (32 octets):
       dcf2a2f66b1657f4265ed5e6ade32d18
       0993d24e2cf7f962649bece68b1fe9ec
     new_accumulator (32 octets):
       7991412622d672f4abf8a4966662e6ea
       890c4ba4143b59f65b1439878118a7e2
     new_mask        (32 octets):
       76615f532f527eaecac7d2fa4232fc32

Sullivan                 Expires 14 January 2027              [Page 150]
Internet-Draft                    raAE                         July 2026

       10596f72a1e2637cfbb5db656d53c9ac
     new_wrapped_acc (32 octets):
       0ff01e750d840c5a613f766c24501ad8
       995524d6b5d93a8aa0a1e2e2ec4b6e4e
     new_snapshot_tag (32 octets):
       bba55e58311ebbd38d7880a9ebec3d19
       3212c0600ce04aeeb7c18ee62e33b9cc

F.17.  Derived-Nonce Cipher Suite Vector

   This vector exercises AES-256-GCM-SIV (Table 10), the MRAE cipher
   suite that uses derived nonce mode.  There is no stored per-segment
   nonce: each nonce is recomputed from nonce_base by the formula in
   Section 4.5.3.2, which XORs uint64((i << 1) | is_final), the segment
   index and finality bit, into the low 8 octets of nonce_base (here Nn
   = 12).  The block applies RewriteSegment to segment 0.  Because the
   nonce is recomputed deterministically, the rewrite reuses the same
   nonce as the original segment 0.  AES-256-GCM-SIV is misuse-
   resistant, so the reuse leaks only equality of identical plaintext-
   and-context pairs, not plaintext.

F.17.1.  Two Segment, AES-256-GCM-SIV, HKDF-SHA-256, 65536 (Rewrite)

   Parameter set:
     cipher suite:
       aead_id       0x001f  (AEAD_AES_256_GCM_SIV)
       kdf_id        0x0001  (HKDF-SHA-256)
     geometry:
       segment_max   65536
     operational:
       epoch_length  0
       nonce_mode    derived
       snap_id       0x0001  (masked multiset hash)

   payload_info (the KDF frames each element):
     aead_id         ( 2 octets): 001f
     segment_max_be  ( 4 octets): 00010000
     kdf_id          ( 2 octets): 0001
     snap_id         ( 2 octets): 0001
     nonce_mode      ( 1 octets): 01
     epoch_length_u8 ( 1 octets): 00
     salt            (32 octets):
       04040404040404040404040404040404
       04040404040404040404040404040404

   Payload schedule:
     commitment   (32 octets):
       bf20f8c7691934f0ccf767b2a5ac19e4

Sullivan                 Expires 14 January 2027              [Page 151]
Internet-Draft                    raAE                         July 2026

       67228674414f68d839a6698a3edd1813
     payload_key  (32 octets):
       d2f4ae67c4024a3b61b902188a75cdc4
       757245350393608e8d4af530b91a4411
     snap_key     (32 octets):
       18e303acfa725f11d6a75a48723fb408
       c481e5703032b0e5f1724461f8901bbc
     nonce_base   (12 octets):
       8a2860a4c1e733427aaa7aeb

   Segment 0 (is_final=0):
     nonce        (12 octets):  (derived from nonce_base, not stored)
       8a2860a4c1e733427aaa7aeb
     epoch_key    (32 octets):
       a65f10ea805ada25b9f1ef7527383cde
       423b104e8813edaed5490747633291c6
     segment_key  (32 octets):
       a65f10ea805ada25b9f1ef7527383cde
       423b104e8813edaed5490747633291c6
     segment_aad  ( 0 octets): (empty)
     ciphertext   (12 octets):
       b3b34ccfb3481851057f1eab
     tag          (16 octets):
       2d143f398a84cdfd47146c194e1c177c
     contrib      (32 octets):
       fce4eed2d488b1ee8994c270cd145202
       ea63b9fd0c0f7401c5cad41c767e91c6
   Segment 1 (is_final=1):
     nonce        (12 octets):  (derived from nonce_base, not stored)
       8a2860a4c1e733427aaa7ae8
     epoch_key    (32 octets):
       3cd69f36c405895994a14b8b5aafaf09
       09517de95d17e6b903cf350fa8769826
     segment_key  (32 octets):
       3cd69f36c405895994a14b8b5aafaf09
       09517de95d17e6b903cf350fa8769826
     segment_aad  ( 0 octets): (empty)
     ciphertext   (20 octets):
       3392acaffbaaa0224644ee4b0efa53ad
       c0d21628
     tag          (16 octets):
       408046d4fb0789a8ae0c41ddb0f66fc3
     contrib      (32 octets):
       7e2e00b404bf5962b6220c4196298f4b
       d25bff62bd9e3230ddd0d344e2b78cc1

   accumulator  (32 octets):
       82caee66d037e88c3fb6ce315b3ddd49

Sullivan                 Expires 14 January 2027              [Page 152]
Internet-Draft                    raAE                         July 2026

       3838469fb1914631181a075894c91d07
   mask         (32 octets):
       b0c48fd39186cb0b55dd036ffd1c46ef
       73c6b282977ecd28e6065cdcc64e36d2
   wrapped_acc  (32 octets):
       320e61b541b123876a6bcd5ea6219ba6
       4bfef41d26ef8b19fe1c5b8452872bd5
   snapshot_tag (32 octets):
       44dea9b3ed7c07f3e95bfe10848430f3
       cc1059251c3cf85d2c2e717634155aaf

   Rewrite of segment 0:
     new_nonce    (12 octets):
       8a2860a4c1e733427aaa7aeb
     new_ciphertext (22 octets):
       f1cb96bd31369de3d8f26e007bf71759
       51a0c59330b0
     new_tag      (16 octets):
       74105b3b59dd624421f67f295921841d
     old_contrib  (32 octets):
       fce4eed2d488b1ee8994c270cd145202
       ea63b9fd0c0f7401c5cad41c767e91c6
     new_contrib  (32 octets):
       64064c4b612f62cafad57b458158d125
       08164dc2bef7594568a978f6f2a1ae55
     acc_delta    (32 octets):
       98e2a299b5a7d3247341b9354c4c8327
       e275f43fb2f82d44ad63acea84df3f93
     new_accumulator (32 octets):
       1a284cff65903ba84cf7770417715e6e
       da4db2a003696b75b579abb210162294
     new_mask        (32 octets):
       5f4185e7b2b940639b4fa672174edf71
       a88c8015423ed35d14134b1741d869a2
     new_wrapped_acc (32 octets):
       4569c918d7297bcbd7b8d176003f811f
       72c132b54157b828a16ae0a551ce4b36
     new_snapshot_tag (32 octets):
       a4ad2d7fa8abbbec998af5c3ced3514f
       e420923c403ba57771bad99a8d03034a

Sullivan                 Expires 14 January 2027              [Page 153]
Internet-Draft                    raAE                         July 2026

F.18.  Cross-Epoch Key Vector

   This vector sets epoch_length = 0, the finest epoch partition, so the
   epoch index equals the segment index and each segment is sealed under
   a distinct epoch key (Section 4.5.2).  The block exposes the
   intermediate epoch_key(i) and segment_key(i) for each segment.  At
   epoch_length = 0 the shift is the identity, so segment_key(i) equals
   epoch_key(i), and epoch_key(0) and epoch_key(1) differ.

F.18.1.  Two Segment, AES-256-GCM, HKDF-SHA-256, 65536, epoch_length 0

   Parameter set:
     cipher suite:
       aead_id       0x0002  (AEAD_AES_256_GCM)
       kdf_id        0x0001  (HKDF-SHA-256)
     geometry:
       segment_max   65536
     operational:
       epoch_length  0
       nonce_mode    random
       snap_id       0x0001  (masked multiset hash)

   payload_info (the KDF frames each element):
     aead_id         ( 2 octets): 0002
     segment_max_be  ( 4 octets): 00010000
     kdf_id          ( 2 octets): 0001
     snap_id         ( 2 octets): 0001
     nonce_mode      ( 1 octets): 00
     epoch_length_u8 ( 1 octets): 00
     salt            (32 octets):
       04040404040404040404040404040404
       04040404040404040404040404040404

   Payload schedule:
     commitment   (32 octets):
       248167fa761884de975ed84dd2464c7b
       0e85cfaf205470750ca644137da76517
     payload_key  (32 octets):
       e27e393efb0b8abec87b27fa0ae3f19c
       0f19093877aae8267d14be74b035eeb6
     snap_key     (32 octets):
       f152bcb8e03852f726a7824c902e9b4f
       aa9b849478cd115c1a3de02b8f04ddb8

   Segment 0 (is_final=0):
     nonce        (12 octets):
       030303030303030303030303
     epoch_key    (32 octets):

Sullivan                 Expires 14 January 2027              [Page 154]
Internet-Draft                    raAE                         July 2026

       cfe9ccdc21e8021fd5cada3fff397f2b
       86431ec14eb0ac60809e4aee4a497f36
     segment_key  (32 octets):
       cfe9ccdc21e8021fd5cada3fff397f2b
       86431ec14eb0ac60809e4aee4a497f36
     segment_aad  (24 octets):
       00095345414c2d444154410008000000
       0000000000000100
     ciphertext   (12 octets):
       04b77b3d7370bf0ce5baeb78
     tag          (16 octets):
       4fd2abdb95a887e32aacced927ea7666
     contrib      (32 octets):
       22f6f5e04dc24fcd3ea57d2aab707c37
       7530de5b5cde77959c85888afe189675
   Segment 1 (is_final=1):
     nonce        (12 octets):
       070707070707070707070707
     epoch_key    (32 octets):
       c6b0ed6d72fe7fe3114623c98f527e3d
       3644cfdc88c56f6e4550495dc92b3fe2
     segment_key  (32 octets):
       c6b0ed6d72fe7fe3114623c98f527e3d
       3644cfdc88c56f6e4550495dc92b3fe2
     segment_aad  (24 octets):
       00095345414c2d444154410008000000
       0000000001000101
     ciphertext   (20 octets):
       550d0675f7ed31bf5377805fcf64bb30
       38167fe1
     tag          (16 octets):
       15c5bbacef49c9e0cc6f4a16a1fb0204
     contrib      (32 octets):
       e9341dfae1376b08122e20e8580b1e94
       7b771e1cf4b11cf5472868cc34a18870

   accumulator  (32 octets):
       cbc2e81aacf524c52c8b5dc2f37b62a3
       0e47c047a86f6b60dbade046cab91e05
   mask         (32 octets):
       4b770b765273b67eda59116abc4521c7
       28998adf914b868f6c94bcdda7f57910
   wrapped_acc  (32 octets):
       80b5e36cfe8692bbf6d24ca84f3e4364
       26de4a983924edefb7395c9b6d4c6715
   snapshot_tag (32 octets):
       7ec104576a2197294a35d2d28000331e
       30dc4172f1f18492950e15ef0c1f1742

Sullivan                 Expires 14 January 2027              [Page 155]
Internet-Draft                    raAE                         July 2026

F.19.  Plaintext-Bound Nonce Component Vectors

   These vectors exercise the optional plaintext-bound nonce
   construction (Appendix A) component by component: the plaintext
   digest pt_digest, the bound digest pt_hash, the framed nonce_ctx, and
   the final nonce.  The construction is encryptor-only and its output
   is indistinguishable from random mode on the wire, so no end-to-end
   vector exercises it.  Component values are the only way to check an
   implementation byte for byte.  The labels are those of Table 26, and
   nonce_ctx enters the final derivation as a single framed info element
   after the payload_info elements.

   Each block reuses the payload_key, payload_info, and salt of a
   published cipher-suite block, so the values chain into the payload
   schedules above.  The random_input value stands in for the fresh
   Random(Nn) draw of the construction, and both blocks bind the
   plaintext "Hello, SEAL!".  These component vectors are printed in
   this appendix only and are not part of the end-to-end corpus.

F.19.1.  AES-256-GCM, HKDF-SHA-256, 16384, Segment 0

   The payload schedule is that of Appendix F.1.

   plaintext    (12 octets):
       48656c6c6f2c205345414c21
   random_input (12 octets):
       0f0f0f0f0f0f0f0f0f0f0f0f
   pt_digest    (32 octets):
       e66fec4cada0ccdb73930622ef393d5b
       a05fb73bdd81205a9f828f75e85ded81
   pt_hash      (32 octets):
       072fa800d5069a226a7322c5b3fb704f
       8564fd2075dd9de6de274a2b5645faaa
   nonce_ctx    (56 octets):
       000a5345414c2d52572d763100080000
       0000000000000020072fa800d5069a22
       6a7322c5b3fb704f8564fd2075dd9de6
       de274a2b5645faaa
   nonce        (12 octets):
       ef044c5a935e8bd52db61582

F.19.2.  AES-256-GCM, TurboSHAKE-256, 65536, Segment 1

   The payload schedule is that of the single-segment TurboSHAKE-256
   block (Appendix F.15).  The segment index is 1, visible in the second
   framed element of nonce_ctx.

Sullivan                 Expires 14 January 2027              [Page 156]
Internet-Draft                    raAE                         July 2026

   plaintext    (12 octets):
       48656c6c6f2c205345414c21
   random_input (12 octets):
       0f0f0f0f0f0f0f0f0f0f0f0f
   pt_digest    (64 octets):
       f809e1b9b0e28d0fd1dce5cf9e4aae59
       fde2b08a551c311b621323a5d2f3c78d
       e55c22edcdd091231c4509849acf592a
       85ab446dfcff4fac008194e6ef59d9b4
   pt_hash      (64 octets):
       ad6a35f94960ee1004391bdfaee16149
       ef56bca4fd5b98abfec56fdb80752624
       ade63d71f3b15e9aa5dd9fb0ffa8e533
       f7ef5caa7620432cf1c1bf7d97611536
   nonce_ctx    (88 octets):
       000a5345414c2d52572d763100080000
       0000000000010040ad6a35f94960ee10
       04391bdfaee16149ef56bca4fd5b98ab
       fec56fdb80752624ade63d71f3b15e9a
       a5dd9fb0ffa8e533f7ef5caa7620432c
       f1c1bf7d97611536
   nonce        (12 octets):
       dbd450bb6f147795caeeee12

F.20.  Digest Transcript Component Vectors

   These vectors exercise the digest transcript (Section 4.7.5)
   component by component: the schedule values of a SEAL-RO-v1 digest
   transcript (snap_id 0x0002) context, two segment leaves over
   specified segment bytes and tags, and the transcript over them.  The
   segment bytes are specified component inputs, not the AEAD output of
   a full object.  The leaf and transcript derivation is what an
   implementation checks byte for byte.  These component vectors are
   printed in this appendix only and are not part of the end-to-end
   corpus.

   The context is a SEAL-RO-v1 object using the digest transcript
   (snap_id 0x0002), with AES-256-GCM and HKDF-SHA-256, the demo CEK
   (0xAA repeated) and salt (0x04 repeated) of Appendix F.1, and the
   default empty G. payload_info is [aead_id 0x0002, segment_max_be
   65536, kdf_id 0x0001, snap_id 0x0002, nonce_mode 0x01 (derived),
   epoch_length_u8 0x20, salt].

Sullivan                 Expires 14 January 2027              [Page 157]
Internet-Draft                    raAE                         July 2026

   CEK          (32 octets):
       aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
       aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
   salt         (32 octets):
       04040404040404040404040404040404
       04040404040404040404040404040404
   commitment   (32 octets):
       86e87a2c11e9b0c0b592c6542fa5bb44
       1df57f06777274295bf803d28287a180
   snap_key     (32 octets):
       ac141c5b853179c84843c8df51b5cc56
       dc4d4a13da95425fb880349e9c508318

F.20.1.  Leaves and Transcript, n_seg = 2

   Segment 0 is non-final with 16 specified ciphertext octets, segment 1
   is final with 8.  The leaf is the ciphertext digest LH(ct_i)
   concatenated with the segment's AEAD tag.  Here the tag is a
   specified component input.  The tag binds the segment's nonce, index,
   and finality (Section 4.7.5).

   ct_0            (16 octets):
       000102030405060708090a0b0c0d0e0f
   tag_0           (16 octets):
       202122232425262728292a2b2c2d2e2f
   leaf(0)=LH(ct_0)||tag_0 (48 octets):
       b726af81e3cadd5c7a486e6a3becc39b
       1c5628da64e8145161124bc172a4ad64
       202122232425262728292a2b2c2d2e2f
   ct_1            (8 octets):
       1011121314151617
   tag_1           (16 octets):
       303132333435363738393a3b3c3d3e3f
   leaf(1)=LH(ct_1)||tag_1 (48 octets):
       3c36a934a52861d5b6c1da66ab493edb
       41f744771e16750ac409b3bed2758f87
       303132333435363738393a3b3c3d3e3f
   transcript      (32 octets):
       2d1cd8ffda1e1e71f09e36fee6b41767
       c43d43caea85dc4e1197ab0b5215369f

   Each leaf is leaf(i) = LH(ct_i) || tag(i).  The transcript's ikm is
   [snap_key] and its info elements are [commitment, uint64(2), leaf(0),
   leaf(1)].  The ordered list binds each leaf's position and the count.
   The stored snapshot value is the transcript.

Sullivan                 Expires 14 January 2027              [Page 158]
Internet-Draft                    raAE                         July 2026

F.21.  Epoch Digest Tree Component Vectors

   These vectors exercise the epoch digest tree (Section 4.7.6) with
   four segments in two epochs.  The context is a SEAL-RO-v1 object
   using the epoch digest tree (snap_id 0x0003) with epoch_length 1
   (epoch_length_u8 0x01), chosen small so the example has two epochs of
   two segments.  Changing snap_id and epoch_length changes
   payload_info, so the commitment and snap_key differ from
   Appendix F.20.  The segment bytes and tags are specified component
   inputs.

   The context uses the same CEK (0xAA repeated) and salt (0x04
   repeated) as Appendix F.20, AES-256-GCM, HKDF-SHA-256, segment_max
   65536, derived nonce, and the default empty G.

   commitment   (32 octets):
       bcb3f7ba5ac54aa98ae62f6500290a40
       3c3dbdd8bf498d24bd12a4280c1fb5bb
   snap_key     (32 octets):
       f618eea83b891b2ab60d311c7be63db5
       28af4126158376c909f7407812ea74fd

   The four leaves, each leaf(i) = LH(ct_i) || tag(i):

   leaf(0) (48 octets):
       b726af81e3cadd5c7a486e6a3becc39b
       1c5628da64e8145161124bc172a4ad64
       202122232425262728292a2b2c2d2e2f
   leaf(1) (48 octets):
       3c36a934a52861d5b6c1da66ab493edb
       41f744771e16750ac409b3bed2758f87
       303132333435363738393a3b3c3d3e3f
   leaf(2) (48 octets):
       2c8e6a5e8019913bebc27623854e35e3
       2bcd1ef439372403f8492e60d571cad1
       404142434445464748494a4b4c4d4e4f
   leaf(3) (48 octets):
       cbd4067f9d6dc18f7bb5f33675a1b01e
       6ce102113947a9dc1045776b8e62d200
       505152535455565758595a5b5c5d5e5f

   Epoch 0 is segments 0 and 1, epoch 1 is segments 2 and 3.  Each epoch
   head is d_e = KDF(protocol_id, "snap_epoch", [snap_key],
   [LH(epoch_run(e))], 32) over that epoch's two concatenated leaves:

Sullivan                 Expires 14 January 2027              [Page 159]
Internet-Draft                    raAE                         July 2026

   d_0 (32 octets):
       fe52c52a3688c6f4e7607f937b19ec94
       b9713e57d0f53e3191c9eed88e2f94c1
   d_1 (32 octets):
       b826e5aa024e5444a82be1c26b097f18
       08d2bd18257b8ccde900dd1c01ab1a7a

   The snapshot is KDF(protocol_id, "snap_epoch_root", [snap_key],
   [commitment, uint64(4), LH(d_0 || d_1)], 32):

   snapshot     (32 octets):
       4c4ca5479c92e989e9fb0a7e9691f34f
       33e87c9ae08af31d24ac4ce408201eb4

F.22.  Negative SnapVerify Vector

   This vector demonstrates the snapshot integrity check.  It takes the
   honest state of the two-segment AES-256-GCM, HKDF-SHA-256, 65536
   vector and flips the first octet of the stored masked accumulator,
   leaving the snapshot tag unchanged.  SnapVerify recomputes the
   snapshot from the two present segment tags (Section 4.7.4): the
   recomputed accumulator is the honest one, so the recomputed snapshot
   tag equals the stored tag, but the recomputed wrapped accumulator
   differs from the tampered one.  The single constant-time comparison
   of the full recomputed snapshot against the stored value therefore
   fails, and SnapVerify returns reject without revealing which half
   differed.  A consumer MUST treat this entry as expect-reject and MUST
   NOT accept it as a valid snapshot.

F.22.1.  Tampered Accumulator, Snapshot Tag Not Recomputed

Sullivan                 Expires 14 January 2027              [Page 160]
Internet-Draft                    raAE                         July 2026

   Source: the two-segment AES-256-GCM, HKDF-SHA-256, 65536 vector;
           the tamper flips the first octet of the stored masked
           accumulator, with the snapshot_tag left unchanged.

   tampered_accumulator (32 octets):
       a463e3d049c025008da67170cb81cbf2
       809f99ea38cca0943f8fd5610a074e0e
   mask                 (32 octets):
       946a2744cf3fd572a03993d434705704
       47d1ce03f4769035a5058780660e5eff
   tampered_wrapped_acc (32 octets):
       3009c49486fff0722d9fe2a4fff19cf6
       c74e57e9ccba30a19a8a52e16c0910f1
   snapshot_tag (32 octets):
       5ce50c9e90db4bbc28297372e401625c
       2e43203ce8008c452ea4355f0941ef67

   SnapVerify recomputes the snapshot from the present segment tags
   and compares it, whole, against the stored value in constant
   time. The recomputed wrapped accumulator differs from the stored
   tampered one, so the comparison fails.

   SnapVerify result: reject

F.23.  SEAL-simple(HKDF-SHA-256, AES-256-GCM)

   This vector realizes the named instantiation of Section 4.12 at
   cipher suite (aead_id 0x0002, kdf_id 0x0001) and matches the
   implementation sketch of Appendix E byte-for-byte.  It uses SEAL-RO-
   v1, snap_id 0x0000 (no snapshot authenticator), derived nonce mode,
   epoch_length 32, segment_max 65536, and the linear immutable
   reduction layout.

Sullivan                 Expires 14 January 2027              [Page 161]
Internet-Draft                    raAE                         July 2026

   Parameter set:
     cipher suite:
       aead_id       0x0002  (AEAD_AES_256_GCM)
       kdf_id        0x0001  (HKDF-SHA-256)
     geometry:
       segment_max   65536
     operational:
       epoch_length  32
       nonce_mode    derived
       snap_id       0x0000  (none)

   payload_info (the KDF frames each element):
     aead_id         ( 2 octets): 0002
     segment_max_be  ( 4 octets): 00010000
     kdf_id          ( 2 octets): 0001
     snap_id         ( 2 octets): 0000
     nonce_mode      ( 1 octets): 01
     epoch_length_u8 ( 1 octets): 20
     salt            (32 octets):
       04040404040404040404040404040404
       04040404040404040404040404040404

   Payload schedule:
     commitment   (32 octets):
       c081f6696c915671a52fc1d6276fdcd7
       ee3bf3a4df4d2651c79e72373f7217ba
     payload_key  (32 octets):
       ee33dbf0003c4bc469fa763c78a041c1
       752ba3e90e2378b4dc7dd9baf54bcc10
     nonce_base   (12 octets):
       552edc38ea645c520b1e5223

   Segment 0 (is_final=1):
     nonce        (12 octets):  (derived, not stored on the wire)
       552edc38ea645c520b1e5222
     segment_aad  ( 0 octets):  (empty; derived mode, empty A_i)
     ciphertext   (12 octets):
       3c77d59ed9ff36062b6dac35
     tag          (16 octets):
       384072dfa1786cd210baff70fd6f8b6e

   Stored object (92 octets, salt || commitment || ct_0 || tag_0):
     0404040404040404040404040404040404040404040404040404040404040404
     c081f6696c915671a52fc1d6276fdcd7ee3bf3a4df4d2651c79e72373f7217ba
     3c77d59ed9ff36062b6dac35
     384072dfa1786cd210baff70fd6f8b6e

Sullivan                 Expires 14 January 2027              [Page 162]
Internet-Draft                    raAE                         July 2026

Acknowledgments

   The author thanks Andrés Fábrega, Thomas Ristenpart, Gregory Rubin,
   Richard Barnes, Brendan McMillion, Thibault Meunier, Kenny Paterson,
   Christopher Patton, Martin Thomson, and Christopher A.  Wood for
   their reviews, comments, and discussions.

Author's Address

   Nick Sullivan
   Cryptography Consulting LLC
   Email: nicholas.sullivan+ietf@gmail.com

Sullivan                 Expires 14 January 2027              [Page 163]