Random-Access Authenticated Encryption
draft-sullivan-cfrg-raae-01
This document is an Internet-Draft (I-D).
Anyone may submit an I-D to the IETF.
This I-D is not endorsed by the IETF and has no formal standing in the
IETF standards process.
| Document | Type | Active Internet-Draft (individual) | |
|---|---|---|---|
| Author | Nick Sullivan | ||
| Last updated | 2026-07-06 | ||
| RFC stream | (None) | ||
| Intended RFC status | (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-01
Crypto Forum N. Sullivan
Internet-Draft Cryptography Consulting LLC
Intended status: Informational 6 July 2026
Expires: 7 January 2027
Random-Access Authenticated Encryption
draft-sullivan-cfrg-raae-01
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 7 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 7 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 . . . . . . . . . . . . . . . . . . . . . . . . 11
3.3. Extended Interface . . . . . . . . . . . . . . . . . . . 12
4. SEAL: A Concrete raAE Construction . . . . . . . . . . . . . 14
4.1. Construction Overview . . . . . . . . . . . . . . . . . . 15
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 . . . . . . . . . . . . . . . . . . . . 21
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 7 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 . . . . . . . . . . . . . . . . . . 29
4.6. Commitment . . . . . . . . . . . . . . . . . . . . . . . 32
4.7. Snapshot Authenticator . . . . . . . . . . . . . . . . . 33
4.7.1. Interface and Requirements . . . . . . . . . . . . . 33
4.7.2. Selecting an Authenticator . . . . . . . . . . . . . 35
4.7.3. None . . . . . . . . . . . . . . . . . . . . . . . . 35
4.7.4. Masked Multiset Hash . . . . . . . . . . . . . . . . 36
4.8. Segment Algorithms . . . . . . . . . . . . . . . . . . . 39
4.8.1. EncryptSegment . . . . . . . . . . . . . . . . . . . 40
4.8.2. DecryptSegment . . . . . . . . . . . . . . . . . . . 40
4.8.3. RewriteSegment . . . . . . . . . . . . . . . . . . . 40
4.9. Top-level Algorithms . . . . . . . . . . . . . . . . . . 41
4.9.1. Read-Only Operations . . . . . . . . . . . . . . . . 41
4.9.2. Rewritable Operations . . . . . . . . . . . . . . . . 45
4.10. SEAL Suites . . . . . . . . . . . . . . . . . . . . . . . 48
4.10.1. Algorithms . . . . . . . . . . . . . . . . . . . . . 48
4.10.2. Composing a SEAL Suite . . . . . . . . . . . . . . . 51
4.11. Serialization Layouts . . . . . . . . . . . . . . . . . . 53
4.11.1. Linear Layout . . . . . . . . . . . . . . . . . . . 53
4.11.2. Aligned Layout . . . . . . . . . . . . . . . . . . . 54
4.11.3. Split Layout . . . . . . . . . . . . . . . . . . . . 57
4.11.4. Immutable-Profile Layouts . . . . . . . . . . . . . 58
4.12. SEAL Named Instantiations . . . . . . . . . . . . . . . . 58
5. Security Analysis . . . . . . . . . . . . . . . . . . . . . . 60
5.1. Operational Summary . . . . . . . . . . . . . . . . . . . 60
5.2. Security Notions . . . . . . . . . . . . . . . . . . . . 61
5.2.1. ra-ROR . . . . . . . . . . . . . . . . . . . . . . . 61
5.2.2. ra-CMT . . . . . . . . . . . . . . . . . . . . . . . 62
5.2.3. Snapshot Authentication . . . . . . . . . . . . . . . 62
5.3. Relations to Other Notions . . . . . . . . . . . . . . . 62
5.4. Adversary Model and Assumptions . . . . . . . . . . . . . 63
5.5. raAE Construction Requirements . . . . . . . . . . . . . 65
5.6. Segment Confidentiality and Integrity . . . . . . . . . . 68
5.7. Commitment Security . . . . . . . . . . . . . . . . . . . 69
5.8. Snapshot Authenticator Security . . . . . . . . . . . . . 71
5.8.1. Masked Multiset Hash . . . . . . . . . . . . . . . . 72
5.9. Capacity and Usage Limits . . . . . . . . . . . . . . . . 72
5.9.1. Accounting Model . . . . . . . . . . . . . . . . . . 72
5.9.2. Limit Classes . . . . . . . . . . . . . . . . . . . . 73
5.9.3. Binding Limits for Profiled Suites . . . . . . . . . 74
5.9.4. Maximum Write-Once Object Size . . . . . . . . . . . 75
5.9.5. Rewrite Capacity . . . . . . . . . . . . . . . . . . 76
5.9.6. Choosing epoch_length . . . . . . . . . . . . . . . . 78
5.9.7. Derivations . . . . . . . . . . . . . . . . . . . . . 79
6. Security Considerations . . . . . . . . . . . . . . . . . . . 81
Sullivan Expires 7 January 2027 [Page 3]
Internet-Draft raAE July 2026
6.1. Detection Summary . . . . . . . . . . . . . . . . . . . . 81
6.2. Nonce Misuse . . . . . . . . . . . . . . . . . . . . . . 83
6.3. Parameter Set Mismatch . . . . . . . . . . . . . . . . . 83
6.4. Framing and Label Errors . . . . . . . . . . . . . . . . 84
6.5. Parameter Misuse . . . . . . . . . . . . . . . . . . . . 84
6.6. Snapshot Integrity Limitations . . . . . . . . . . . . . 85
6.7. Salt Reuse . . . . . . . . . . . . . . . . . . . . . . . 86
6.8. Rewrite Hazards . . . . . . . . . . . . . . . . . . . . . 86
6.9. Constant-Time Implementation . . . . . . . . . . . . . . 88
6.10. Properties Not Provided . . . . . . . . . . . . . . . . . 88
6.11. Cross-Application Domain Separation . . . . . . . . . . . 89
7. IANA Considerations . . . . . . . . . . . . . . . . . . . . . 89
8. References . . . . . . . . . . . . . . . . . . . . . . . . . 90
8.1. Normative References . . . . . . . . . . . . . . . . . . 90
8.2. Informative References . . . . . . . . . . . . . . . . . 91
Appendix A. Optional Plaintext-Bound Nonce Construction . . . . 93
Appendix B. Optional Hedged Randomness . . . . . . . . . . . . . 94
Appendix C. Proof Status and Security Claim Provenance . . . . . 95
C.1. Provenance of the Security Claims . . . . . . . . . . . . 96
C.2. Advantage Notation and Adversary Parameters . . . . . . . 98
C.3. ra-ROR Reduction . . . . . . . . . . . . . . . . . . . . 99
C.4. Commitment . . . . . . . . . . . . . . . . . . . . . . . 100
C.5. Snapshot Authenticator . . . . . . . . . . . . . . . . . 100
C.6. Nonce Modes . . . . . . . . . . . . . . . . . . . . . . . 102
Appendix D. Design Rationale . . . . . . . . . . . . . . . . . . 102
D.1. Key Schedule Design Rationale . . . . . . . . . . . . . . 102
D.2. Nonce Mode Design Rationale . . . . . . . . . . . . . . . 103
D.3. Snapshot Authenticator Design Rationale . . . . . . . . . 103
Appendix E. Test Vectors . . . . . . . . . . . . . . . . . . . . 104
E.1. Single Segment, AES-256-GCM, HKDF-SHA-256, 16384 . . . . 106
E.2. Commitment with Global Associated Data . . . . . . . . . 107
E.3. KDF Combiner Vectors . . . . . . . . . . . . . . . . . . 108
E.4. Single Segment, AES-256-GCM, HKDF-SHA-256, 65536 . . . . 110
E.5. Single Segment, ChaCha20-Poly1305, HKDF-SHA-256, 16384 . 111
E.6. Single Segment, ChaCha20-Poly1305, HKDF-SHA-256, 65536 . 112
E.7. Single Segment, AEGIS-256, HKDF-SHA-256, 16384 . . . . . 114
E.8. Single Segment, AEGIS-256, HKDF-SHA-256, 65536 . . . . . 115
E.9. Two Segment, AES-256-GCM, HKDF-SHA-256, 16384 . . . . . . 116
E.10. Two Segment, AES-256-GCM, HKDF-SHA-256, 65536 . . . . . . 118
E.11. Two Segment, ChaCha20-Poly1305, HKDF-SHA-256, 16384 . . . 119
E.12. Two Segment, ChaCha20-Poly1305, HKDF-SHA-256, 65536 . . . 121
E.13. Two Segment, AEGIS-256, HKDF-SHA-256, 16384 . . . . . . . 122
E.14. Two Segment, AEGIS-256, HKDF-SHA-256, 65536 . . . . . . . 124
E.15. TurboSHAKE-256 Cipher Suite Vectors . . . . . . . . . . . 126
E.15.1. Single Segment, AES-256-GCM, TurboSHAKE-256,
65536 . . . . . . . . . . . . . . . . . . . . . . . . 126
E.15.2. Two Segment, AES-256-GCM, TurboSHAKE-256, 65536 . . 127
E.16. Segment Rewrite Vector . . . . . . . . . . . . . . . . . 130
Sullivan Expires 7 January 2027 [Page 4]
Internet-Draft raAE July 2026
E.16.1. Two Segment, AES-256-GCM, HKDF-SHA-256, 65536
(Rewrite) . . . . . . . . . . . . . . . . . . . . . . 130
E.17. Derived-Nonce Cipher Suite Vector . . . . . . . . . . . . 132
E.17.1. Two Segment, AES-256-GCM-SIV, HKDF-SHA-256, 65536
(Rewrite) . . . . . . . . . . . . . . . . . . . . . . 132
E.18. Cross-Epoch Key Vector . . . . . . . . . . . . . . . . . 135
E.18.1. Two Segment, AES-256-GCM, HKDF-SHA-256, 65536,
epoch_length 0 . . . . . . . . . . . . . . . . . . . 135
E.19. Plaintext-Bound Nonce Component Vectors . . . . . . . . . 137
E.19.1. AES-256-GCM, HKDF-SHA-256, 16384, Segment 0 . . . . 137
E.19.2. AES-256-GCM, TurboSHAKE-256, 65536, Segment 1 . . . 138
E.20. Negative SnapVerify Vector . . . . . . . . . . . . . . . 138
E.20.1. Tampered Accumulator, Snapshot Tag Not Recomputed . 138
Appendix F. Changes from draft-sullivan-cfrg-raae-00 . . . . . . 139
Acknowledgments . . . . . . . . . . . . . . . . . . . . . . . . . 141
Author's Address . . . . . . . . . . . . . . . . . . . . . . . . 141
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.
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 tag and the count into
a public snapshot value that an adversary cannot forge without the
content-derived key.
Sullivan Expires 7 January 2027 [Page 5]
Internet-Draft raAE July 2026
.------------------------------.
| 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:
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 tags and the
count are exactly the set 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
Sullivan Expires 7 January 2027 [Page 6]
Internet-Draft raAE July 2026
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
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's snapshot authenticator 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.
Sullivan Expires 7 January 2027 [Page 7]
Internet-Draft raAE July 2026
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.
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.
Sullivan Expires 7 January 2027 [Page 8]
Internet-Draft raAE July 2026
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.
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.
Sullivan Expires 7 January 2027 [Page 9]
Internet-Draft raAE July 2026
Per-segment authenticity: AEAD verification for one segment only.
Snapshot integrity: Verification of the complete segment tag set and
snapshot.
External freshness: Rollback protection supplied outside raAE.
2.2. 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,
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).
Sullivan Expires 7 January 2027 [Page 10]
Internet-Draft raAE July 2026
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.
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
Sullivan Expires 7 January 2027 [Page 11]
Internet-Draft raAE July 2026
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.
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.
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'.
Sullivan Expires 7 January 2027 [Page 12]
Internet-Draft raAE July 2026
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
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.
Sullivan Expires 7 January 2027 [Page 13]
Internet-Draft raAE July 2026
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.
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 tag feeds it to produce the snapshot.
Sullivan Expires 7 January 2027 [Page 14]
Internet-Draft raAE July 2026
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
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:
Sullivan Expires 7 January 2027 [Page 15]
Internet-Draft raAE July 2026
+============+==========================================+
| 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.
+===================+===================+=============+==========+
| Parameter | Type or range | Set by | Defined |
| | | | in |
+===================+===================+=============+==========+
| AEAD | RFC 5116 | suite | Section |
| | algorithm | | 4.10.1 |
+-------------------+-------------------+-------------+----------+
| KDF | kdf_id from | suite | Section |
| | Table 10 | | 4.3 |
+-------------------+-------------------+-------------+----------+
| segment_max | power of two, at | suite | Section |
| | least 4096 | | 4.10 |
Sullivan Expires 7 January 2027 [Page 16]
Internet-Draft raAE July 2026
+-------------------+-------------------+-------------+----------+
| 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 |
+-------------------+-------------------+-------------+----------+
| 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 |
+-------------------+-------------------+-------------+----------+
Sullivan Expires 7 January 2027 [Page 17]
Internet-Draft raAE July 2026
| 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 10 for the permitted KDFs.
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
Sullivan Expires 7 January 2027 [Page 18]
Internet-Draft raAE July 2026
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 9), 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 9 and Table 10. 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
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
Sullivan Expires 7 January 2027 [Page 19]
Internet-Draft raAE July 2026
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)
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.
Sullivan Expires 7 January 2027 [Page 20]
Internet-Draft raAE July 2026
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).
The KDF is built from the cipher suite's hash in one of two forms.
The cipher suite's kdf_id (Table 10) selects which one.
4.3.1. Two-Step KDF
Sullivan Expires 7 January 2027 [Page 21]
Internet-Draft raAE July 2026
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)
Sullivan Expires 7 January 2027 [Page 22]
Internet-Draft raAE July 2026
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 E.3). The
two forms are not mutually injective: a cipher suite's kdf_id
(Table 10) 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 E.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 E.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
Sullivan Expires 7 January 2027 [Page 23]
Internet-Draft raAE July 2026
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 9 (for example, 0x0002 for AES-256-GCM).
* 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 10 (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 11 (0x0000 for none, 0x0001 for the masked
multiset hash).
* nonce_mode: 1 octet, the nonce construction (Table 12): 0x00 for
random, 0x01 for derived. Table 9 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 9. 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).
Sullivan Expires 7 January 2027 [Page 24]
Internet-Draft raAE July 2026
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)
+=============+======================+============================+
| | 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
Sullivan Expires 7 January 2027 [Page 25]
Internet-Draft raAE July 2026
(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.
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" |
+----------------------------+-------------------+---------------+
Sullivan Expires 7 January 2027 [Page 26]
Internet-Draft raAE July 2026
| Nonce base | nonce_base_label | "nonce_base" |
+----------------------------+-------------------+---------------+
| Epoch key | epoch_key_label | "epoch_key" |
+----------------------------+-------------------+---------------+
Table 4: SEAL core KDF labels by role
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 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.
Sullivan Expires 7 January 2027 [Page 27]
Internet-Draft raAE July 2026
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)
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)
Sullivan Expires 7 January 2027 [Page 28]
Internet-Draft raAE July 2026
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
The two modes differ as follows:
Sullivan Expires 7 January 2027 [Page 29]
Internet-Draft raAE July 2026
+===============+===================+===========================+
| 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 7 January 2027 [Page 30]
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 7 January 2027 [Page 31]
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 7 January 2027 [Page 32]
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 set of segment tags into
the public snapshot value. A SEAL profile selects one with snap_id
(Table 11).
4.7.1. Interface and Requirements
A snapshot authenticator provides the following operations:
add(i, tag): Fold one segment's tag into the running value.
remove(i, tag): Take one segment's tag back out. An updatable
authenticator provides this operation, so a single segment can be
rewritten or dropped without rebuilding. An add-only
authenticator omits it and rebuilds the value by re-adding the
surviving segments.
set_length(n): Set the segment count to n.
snapshot(): Bind the current state into the snapshot value, which
authenticates the (index, tag) set and the count n_seg.
verify(snapshot): Recompute the snapshot value over the current
(index, tag) set and accept only if it matches the stored
snapshot, in constant time.
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, tag) set and the
count n_seg.
Sullivan Expires 7 January 2027 [Page 33]
Internet-Draft raAE July 2026
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,
tag) against the recorded set. The masked multiset hash reads all
tags and supports the whole-object target only. 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,
tag) 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 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 tag. 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
tag changes:
1. Under the current count, remove each changed segment's current
tag (remove(i, tag)). 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 tag (add(i, tag)). 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.
Sullivan Expires 7 January 2027 [Page 34]
Internet-Draft raAE July 2026
Each operation is an instance of that shape over different segments:
+===========+===========+=============+==============+==============+
| Operation | Count | Tags | Tags added | Terminal re- |
| | | removed | | 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
Each operation re-encrypts only the segments whose tag changes, so a
caller that cannot recover a re-encrypted segment's plaintext cannot
perform it.
This sequence uses remove, so it is the updatable-authenticator path.
An add-only authenticator 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 11): 0x0000 for none, or 0x0001 for the masked multiset hash.
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).
Sullivan Expires 7 January 2027 [Page 35]
Internet-Draft raAE July 2026
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.
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
Sullivan Expires 7 January 2027 [Page 36]
Internet-Draft raAE July 2026
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:
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.
Sullivan Expires 7 January 2027 [Page 37]
Internet-Draft raAE July 2026
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:
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.
Sullivan Expires 7 January 2027 [Page 38]
Internet-Draft raAE July 2026
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
(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 7: 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
Sullivan Expires 7 January 2027 [Page 39]
Internet-Draft raAE July 2026
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).
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 9 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 folds tag_i into the snapshot authenticator with add(i,
tag_i) (Section 4.7). 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.
Sullivan Expires 7 January 2027 [Page 40]
Internet-Draft raAE July 2026
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.
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)
(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 (add(i, tag), remove(i, tag),
set_length(n), snapshot(), and verify()) without referring to how any
is computed. The authenticator selected by snap_id (Table 11)
realizes those operations. For the masked multiset hash they reduce
to the accumulator, snapshot-tag, and mask formulas of Section 4.7.4.
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 11), 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, under a snapshot authenticator, adds its
tag with add(i, tag). Then snapshot() binds the whole set into the
stored snapshot value. Because the segments are independent, the
second phase may run in any order or in parallel.
Sullivan Expires 7 January 2027 [Page 41]
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 4: Key Schedule and Segment Encryption
The per-segment tags then feed the snapshot authenticator, which adds
each one and binds them into the snapshot value (Section 4.7).
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):
add(i, tag_i)
if snapshot authentication is enabled (snap_id != 0x0000):
set_length(n_seg)
snapshot = snapshot()
store snapshot
A zero-length plaintext produces a single segment of length 0 with
is_final = 1.
Sullivan Expires 7 January 2027 [Page 42]
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 every tag. 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 all tags are 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 (index, tag) pairs.
SnapVerify needs every tag, so a reader that streams segments in
order cannot gate their release on snapshot integrity without first
buffering all tags or using a layout that carries the tags 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.
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.
Sullivan Expires 7 January 2027 [Page 43]
Internet-Draft raAE July 2026
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 all segment tags are
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:
+===========================+===============================+
| 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 8: Decryption failure conditions
Sullivan Expires 7 January 2027 [Page 44]
Internet-Draft raAE July 2026
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,
which configures a snapshot authenticator (snap_id = 0x0001). Each
re-encrypts only the segments whose tag changes and updates the
snapshot over the new segment set. 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 and never reads another.
Replacing a segment removes its old tag from the snapshot
authenticator and adds the new one, then updates the snapshot over
the unchanged count with snapshot(). 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). This single-segment update and the trust in
stored snapshot state are properties of an updatable authenticator
like the masked multiset hash, which folds the change in O(1). An
add-only authenticator re-reads the surviving tags to rebuild on
rewrite.
The procedure is:
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.
Sullivan Expires 7 January 2027 [Page 45]
Internet-Draft raAE July 2026
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 5). 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.
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 5: Append and Truncate
Sullivan Expires 7 January 2027 [Page 46]
Internet-Draft raAE July 2026
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
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.
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.
Sullivan Expires 7 January 2027 [Page 47]
Internet-Draft raAE July 2026
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 E, 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.
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).
Sullivan Expires 7 January 2027 [Page 48]
Internet-Draft raAE July 2026
+===================+=========+====+====+=========+==============+
| 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 9: 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.
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).
Sullivan Expires 7 January 2027 [Page 49]
Internet-Draft raAE July 2026
SEAL permits the KDF cipher suites in Table 10, 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 10: Permitted SEAL KDF Cipher Suites
HKDF-SHA-256 is specified in [RFC5869]. TurboSHAKE-256 is the
extendable-output mode of the SHA-3 family selected by
[I-D.ietf-hpke-pq]. 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:
+=========+======================+===========================+
| snap_id | Name | Snapshot value |
+=========+======================+===========================+
| 0x0000 | none | none produced |
+---------+----------------------+---------------------------+
| 0x0001 | masked multiset hash | snapshot, per Section 4.7 |
+---------+----------------------+---------------------------+
Table 11: SEAL Snapshot Authenticators
Sullivan Expires 7 January 2027 [Page 50]
Internet-Draft raAE July 2026
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. A
profile that supports rewrite requires snap_id = 0x0001, so every
rewritable object carries whole-object integrity (Section 4.9.2).
The nonce mode is carried explicitly by nonce_mode:
+============+=========+
| nonce_mode | Name |
+============+=========+
| 0x00 | random |
+------------+---------+
| 0x01 | derived |
+------------+---------+
Table 12: 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.
4.10.2. Composing a SEAL Suite
A SEAL suite fixes an AEAD and a KDF (Table 9, Table 10), a maximum
segment size, a snapshot authenticator (snap_id, Table 11), a nonce
mode (nonce_mode, Table 12), 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 snap_id
0x0001 (the masked multiset hash), so every rewritable object carries
whole-object integrity, and permits a random nonce or a derived nonce
with an MRAE AEAD. It supports rewrite, extend, and truncate, and
Sullivan Expires 7 January 2027 [Page 51]
Internet-Draft raAE July 2026
carries SEAL's snapshot machinery: the accumulator, its mask, the
snapshot value, and 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 tamper-evidence
guarantee. It selects a derived nonce and sets snap_id to 0x0000, so
that no snapshot authenticator runs, and it is write-once: an
encryptor MUST NOT rewrite a segment once it has been written. The
rule is load-bearing: a rewrite would repeat the segment's derived
nonce, with the consequences given in Section 6.2. Because each
derived nonce is then used only once, the profile works with any
AEAD, including a non-MRAE one. See 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, but not snapshot or whole-object
integrity. Whole-object integrity, when needed, comes from the
snapshot in the mutable profile or a layer above SEAL. Truncation
detection 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.
+=============+===================+=========+=================+
| protocol_id | nonce_mode | snap_id | mutability |
+=============+===================+=========+=================+
| SEAL-RW-v1 | random or derived | 0x0001 | rewrite/extend/ |
| | | | truncate |
+-------------+-------------------+---------+-----------------+
| SEAL-RO-v1 | derived | 0x0000 | write-once |
+-------------+-------------------+---------+-----------------+
Table 13: 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.
Sullivan Expires 7 January 2027 [Page 52]
Internet-Draft raAE July 2026
SEAL-RW-v1 (mutable) fits content updated in place: editable files,
mutable object stores, and append-or-truncate logs. It carries
snap_id 0x0001, 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).
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.
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 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
This section describes three serialization layouts a consuming
protocol can use to store raAE output: linear, aligned, and split.
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.
Sullivan Expires 7 January 2027 [Page 53]
Internet-Draft raAE July 2026
+------+------------+---------------+----------+-----+----------+
| salt | commitment | snapshot | segment | ... | segment |
| (32) | (Nh) | (Na) | 0 data | | n_seg-1 |
| | | | [nonce] | | [nonce] |
| | | | ct + tag | | ct + tag |
+------+------------+---------------+----------+-----+----------+
Figure 6: Linear Layout
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; the ciphertext follows the header.
Sullivan Expires 7 January 2027 [Page 54]
Internet-Draft raAE July 2026
+--------------------------------------------+
| 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 7: Aligned Layout
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, and
one metadata entry per segment:
+------+------------+---------------+----------------------+
| salt | commitment | snapshot | per-segment metadata |
| (32) | (Nh) | (Na) | (n_seg * meta_len) |
+------+------------+---------------+----------------------+
Figure 8: Aligned Layout Header
Sullivan Expires 7 January 2027 [Page 55]
Internet-Draft raAE July 2026
Each metadata entry holds the segment's stored nonce and its AEAD
tag:
+------------+-----------+
| [nonce(i)] | tag(i) |
| (Nn) | (Nt = 16) |
+------------+-----------+
Figure 9: Metadata Entry
The bracketed nonce is present only in random nonce mode. The tag is
always present, Nt = 16 octets in every SEAL suite (Table 9). Each
metadata entry holds the Np-octet presented nonce and the Nt-octet
tag, so meta_len = Np + Nt octets. 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 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 14: Metadata entry size by AEAD
The n_seg entries total n_seg * meta_len octets, so the whole header
is 32 + commitment_length + Na + n_seg * meta_len octets. With the
default commitment_length = Nh and the masked multiset hash's Na = 2
* Nh, the header is 32 + 3 * Nh + n_seg * meta_len octets.
A reader verifies the commitment and the snapshot value from the
header alone, then seeks to any segment using these offsets. Because
the snapshot is computed from the per-segment tags alone (the
snapshot authenticator takes the segment tag, not the ciphertext),
and the header holds every tag in its metadata entries, a reader
authenticates the whole object's snapshot from the header without
reading or streaming any ciphertext. This supports efficient random-
access reads.
Sullivan Expires 7 January 2027 [Page 56]
Internet-Draft raAE July 2026
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 9), and the snapshot value last.
Because the snapshot is computed from the per-segment tags alone
(Section 4.7.4) and every tag lives in the metadata stream, a reader
authenticates the snapshot by reading only the metadata stream, never
the data stream that holds the ciphertext.
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 10: 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 7 January 2027 [Page 57]
Internet-Draft raAE July 2026
4.11.4. Immutable-Profile Layouts
Under an immutable profile (SEAL-RO-v1: snap_id = 0x0000 with a
derived nonce), two fields drop out of the layouts above. No
snapshot authenticator runs, so there is no snapshot value, and each
nonce is recomputed from the key schedule, so Np = 0 and no nonce is
stored. A metadata entry is then 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 headers reduce to 32 +
commitment_length + Nt * n_seg octets, the salt and commitment
followed by one tag per segment. 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, with no shared snapshot to read or maintain.
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.
+=================+==========+===========+==========+=====+=======+
| Name |Profile |segment_max|nonce_mode|epoch|layout |
+=================+==========+===========+==========+=====+=======+
| SEAL-attachment |SEAL-RO-v1|65536 |derived |32 |linear |
+-----------------+----------+-----------+----------+-----+-------+
| SEAL-simple |SEAL-RW-v1|65536 |random |16 |linear |
+-----------------+----------+-----------+----------+-----+-------+
| SEAL-memory |SEAL-RW-v1|16384 |random |16 |aligned|
+-----------------+----------+-----------+----------+-----+-------+
| SEAL-disk |SEAL-RW-v1|16384 |random |16 |split |
+-----------------+----------+-----------+----------+-----+-------+
| SEAL-compact |SEAL-RW-v1|16384 |derived |16 |aligned|
+-----------------+----------+-----------+----------+-----+-------+
Table 15: SEAL named instantiations
Each instantiation is named for its primary use case, described
below. The snapshot authenticator follows the profile: SEAL-RO-v1
sets snap_id 0x0000 and SEAL-RW-v1 sets snap_id 0x0001
(Section 4.10.2).
Sullivan Expires 7 January 2027 [Page 58]
Internet-Draft raAE July 2026
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-attachment performs no rewrites, so
it takes a larger epoch (32), while 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 9), 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
an in-place rewrite reuses that nonce, which requires an MRAE AEAD
(Section 4.10.2). The write-once SEAL-attachment and the random-
nonce SEAL-simple, SEAL-memory, and SEAL-disk admit any AEAD.
SEAL-attachment 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-simple 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
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.
Sullivan Expires 7 January 2027 [Page 59]
Internet-Draft raAE July 2026
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
(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.
Sullivan Expires 7 January 2027 [Page 60]
Internet-Draft raAE July 2026
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.
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.
Sullivan Expires 7 January 2027 [Page 61]
Internet-Draft raAE July 2026
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]).
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
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
Sullivan Expires 7 January 2027 [Page 62]
Internet-Draft raAE July 2026
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.
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.
Sullivan Expires 7 January 2027 [Page 63]
Internet-Draft raAE July 2026
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 and, in
random nonce mode, per-segment nonces.
snapshot: Stored object metadata. It authenticates the segment tag
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.
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.
Sullivan Expires 7 January 2027 [Page 64]
Internet-Draft raAE July 2026
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 (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.
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
Sullivan Expires 7 January 2027 [Page 65]
Internet-Draft raAE July 2026
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 set
of segment tags and the segment count into a public snapshot value
that an adversary without the authenticator's secret key cannot
forge for a modified set of tags 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.
SEAL's construction (Section 4) satisfies these requirements. Its
KDF (Section 4.3), any of the cipher suites in Table 10 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 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.
Sullivan Expires 7 January 2027 [Page 66]
Internet-Draft raAE July 2026
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. No SEAL input
approaches 0xFFFE octets, so for SEAL frame is byte-identical to a
plain 2-octet length prefix and LH is never invoked.
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.
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 10.
nonce_mode: A per-object payload_info field constrained by the
profile, not a suite-fixed lock. Table 9 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 9. 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.
Sullivan Expires 7 January 2027 [Page 67]
Internet-Draft raAE July 2026
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,
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
Sullivan Expires 7 January 2027 [Page 68]
Internet-Draft raAE July 2026
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.
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.
Sullivan Expires 7 January 2027 [Page 69]
Internet-Draft raAE July 2026
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
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
Sullivan Expires 7 January 2027 [Page 70]
Internet-Draft raAE July 2026
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]).
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. It gives no protection among
parties that share the CEK, because any CEK-holder can recompute
snap_key, and it is not a third-party-verifiable commitment to a
segment set. An application that needs sender attribution or third-
party verifiability MUST add a signing layer over the snapshot
context.
Per-segment AEAD binds each segment's index and finality bit. The
snapshot authenticator adds authentication of the full set of segment
tags 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.
Sullivan Expires 7 January 2027 [Page 71]
Internet-Draft raAE July 2026
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.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.
write (initial write): the first encryption of a segment.
rewrite: a later encryption of the same segment.
Sullivan Expires 7 January 2027 [Page 72]
Internet-Draft raAE July 2026
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 16: Usage-limit classes
Sullivan Expires 7 January 2027 [Page 73]
Internet-Draft raAE July 2026
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
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 7 January 2027 [Page 74]
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 17: 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 7 January 2027 [Page 75]
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 18: 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 17).
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 7 January 2027 [Page 76]
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 19: 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 20: 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 7 January 2027 [Page 77]
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.
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.
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.
Sullivan Expires 7 January 2027 [Page 78]
Internet-Draft raAE July 2026
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
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.
Sullivan Expires 7 January 2027 [Page 79]
Internet-Draft raAE July 2026
SEAL implementations MUST compute usage budgets from the AEAD-
specific confidentiality and integrity bounds, not from nonce-
collision probability alone. Table 17 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.
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
Sullivan Expires 7 January 2027 [Page 80]
Internet-Draft raAE July 2026
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 17). 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
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 | |
Sullivan Expires 7 January 2027 [Page 81]
Internet-Draft raAE July 2026
| 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 | |
+--------------+--------------------------------------------------+
| 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. |
+--------------+--------------------------------------------------+
Sullivan Expires 7 January 2027 [Page 82]
Internet-Draft raAE July 2026
| 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 21: 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.
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
Sullivan Expires 7 January 2027 [Page 83]
Internet-Draft raAE July 2026
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 9). 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:
+============+==============+====================================+
| AEAD class | random nonce | derived nonce |
+============+==============+====================================+
| non-MRAE | valid | mutable: unsafe; write-once: valid |
+------------+--------------+------------------------------------+
| MRAE | wasteful | valid |
+------------+--------------+------------------------------------+
Table 22: 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.
Sullivan Expires 7 January 2027 [Page 84]
Internet-Draft raAE July 2026
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 over the segment tags authenticates
the tag set, its positions, and the count. It does not confirm that
a tag is a valid AEAD tag for the ciphertext beside it. Decrypting
that segment is what establishes that, so snapshot verification
layers set, position, and count binding on top of per-segment AEAD
without replacing it.
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.
Sullivan Expires 7 January 2027 [Page 85]
Internet-Draft raAE July 2026
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.
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.
Sullivan Expires 7 January 2027 [Page 86]
Internet-Draft raAE July 2026
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 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.
Sullivan Expires 7 January 2027 [Page 87]
Internet-Draft raAE July 2026
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
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).
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.
Sullivan Expires 7 January 2027 [Page 88]
Internet-Draft raAE July 2026
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
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 early allocations in their respective
registries. Each registry reference firms up when the referenced I-D
is published as an RFC, and the corresponding SEAL normative
reference is a downref under IRTF stream conventions until then.
+==============+======+================+============================+
| Registry |Code | Algorithm | Reference |
| |point | | |
+==============+======+================+============================+
| AEAD |0x0021| AEGIS-256 | [I-D.irtf-cfrg-aegis-aead] |
| Algorithms | | | |
| ([RFC5116]) | | | |
+--------------+------+----------------+----------------------------+
| AEAD |0x0024| AEGIS-256X2 | [I-D.irtf-cfrg-aegis-aead] |
| Algorithms | | | |
| ([RFC5116]) | | | |
+--------------+------+----------------+----------------------------+
| HPKE KDF |0x0013| TurboSHAKE-256 | [I-D.ietf-hpke-pq] |
| ([RFC9180] | | | |
| Section | | | |
| 7.2) | | | |
+--------------+------+----------------+----------------------------+
Table 23: Early-Allocation Code Points
Future SEAL profiles MAY consume additional entries from either
registry without revising this document.
Sullivan Expires 7 January 2027 [Page 89]
Internet-Draft raAE July 2026
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-04, 2 March
2026, <https://datatracker.ietf.org/doc/html/draft-ietf-
hpke-pq-04>.
[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>.
Sullivan Expires 7 January 2027 [Page 90]
Internet-Draft raAE July 2026
[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>.
[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>.
[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>.
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>.
Sullivan Expires 7 January 2027 [Page 91]
Internet-Draft raAE July 2026
[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-
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>.
[SEALPROOFS]
TBD, "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 7 January 2027 [Page 92]
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 24: 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 E.19.
Sullivan Expires 7 January 2027 [Page 93]
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 7 January 2027 [Page 94]
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 snapshot integrity as
an extension of the ra-ROR framework, 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 7 January 2027 [Page 95]
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, and only for
over-large fields. 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 |
|associated- |base proof; | | commitment bound for the |
|data and |SEAL | | forged position |
|position |position- | | |
Sullivan Expires 7 January 2027 [Page 96]
Internet-Draft raAE July 2026
|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 |
+------------+--------------+--------+------------------------------+
|AEAD usage, |external work |Section | [I-D.irtf-cfrg-aead-limits], |
|rewrite, and|(operational, |5.9, | [RFC8452] Section 9 with |
|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) | | | |
+------------+--------------+--------+------------------------------+
Sullivan Expires 7 January 2027 [Page 97]
Internet-Draft raAE July 2026
Table 25: 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
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
Sullivan Expires 7 January 2027 [Page 98]
Internet-Draft raAE July 2026
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 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).
Sullivan Expires 7 January 2027 [Page 99]
Internet-Draft raAE July 2026
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
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.
Sullivan Expires 7 January 2027 [Page 100]
Internet-Draft raAE July 2026
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).
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-
Sullivan Expires 7 January 2027 [Page 101]
Internet-Draft raAE July 2026
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]).
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.
Sullivan Expires 7 January 2027 [Page 102]
Internet-Draft raAE July 2026
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.
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 9 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
Sullivan Expires 7 January 2027 [Page 103]
Internet-Draft raAE July 2026
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).
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).
Appendix E. Test Vectors
This appendix is informative.
All vectors use protocol_id = "SEAL-RW-v1", 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 9 and Table 10. 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 E.17 instead recomputes each nonce from
nonce_base, and the cross-epoch vector in Appendix E.18 sets
epoch_length 0 and exposes the intermediate epoch_key and segment_key
for each segment. Appendix E.1 gives the full KDF trace for one
commitment. The other blocks list schedule outputs only. The
eighteen computed positive vectors, plus the negative SnapVerify
vector in Appendix E.20, are published byte-for-byte as raae-
v1-vectors.json in the draft repository.
The vectors are organized by purpose:
Sullivan Expires 7 January 2027 [Page 104]
Internet-Draft raAE July 2026
Annotated walkthrough: Appendix E.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 E.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.
KDF coverage: Appendix E.15 covers the TurboSHAKE-256 KDF suite and
its 64-octet Nh outputs.
Rewrite coverage: Appendix E.16 shows a segment rewrite and the
corresponding accumulator and snapshot_tag update.
Derived-nonce coverage: Appendix E.17 covers AES-256-GCM-SIV in
derived nonce mode, where the nonce is recomputed rather than
stored.
Epoch coverage: Appendix E.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 E.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 E.2 pins the commitment's
G input at its empty default, which equals the Appendix E.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 E.20 changes the accumulator without
recomputing the snapshot tag; SnapVerify rejects the stored
snapshot.
Sullivan Expires 7 January 2027 [Page 105]
Internet-Draft raAE July 2026
The JSON file is the complete corpus for automated tests. The text
below is intended for debugging and review.
E.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):
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
Sullivan Expires 7 January 2027 [Page 106]
Internet-Draft raAE July 2026
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
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
E.2. Commitment with Global Associated Data
These vectors pin the G input (Section 4.6) against the schedule of
Appendix E.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 E.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.
Sullivan Expires 7 January 2027 [Page 107]
Internet-Draft raAE July 2026
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
E.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)):
Sullivan Expires 7 January 2027 [Page 108]
Internet-Draft raAE July 2026
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
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
Sullivan Expires 7 January 2027 [Page 109]
Internet-Draft raAE July 2026
03040500020040
output (64 octets):
02958e6256193c71b0cc4b8ac3273b31
15d1ac30fd9aab537ad6916206be9828
9146b56526054b3c11e22f2c375d0b24
43e8d2ad2e6a89b874aa9ef4ce2c9c77
E.4. Single 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=1):
nonce (12 octets):
030303030303030303030303
segment_aad (24 octets):
00095345414c2d444154410008000000
0000000000000101
Sullivan Expires 7 January 2027 [Page 110]
Internet-Draft raAE July 2026
ciphertext (12 octets):
1815f12b13f7ee2532f0fcca
tag (16 octets):
df4b1428af3c5ecb6d804159fec249e0
contrib (32 octets):
73e62d2574a38dc44b406a0c2f2d57b1
2b7ca777b053cdbb4e9d6f1b3257991a
accumulator (32 octets):
73e62d2574a38dc44b406a0c2f2d57b1
2b7ca777b053cdbb4e9d6f1b3257991a
mask (32 octets):
dfd59806ded61dbf83bab8a7e143da0e
01714d207c2f53af86e00c875590093b
wrapped_acc (32 octets):
ac33b523aa75907bc8fad2abce6e8dbf
2a0dea57cc7c9e14c87d639c67c79021
snapshot_tag (32 octets):
5a7713eab7ce2f7f246647aa407e14fa
f295a04333f06c27cdc1193252a9b8bc
E.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
Sullivan Expires 7 January 2027 [Page 111]
Internet-Draft raAE July 2026
6a050b24f372ecfb79a66b3f2fdc1acb
payload_key (32 octets):
d0f1d392a371642db684a23858c0193c
2d7406cb4360c81ef9190391cacf885f
snap_key (32 octets):
b156708dc559791d78014bae5e01b5fd
f8a397c2d140fd9b9468e3cceeb8aa5d
Segment 0 (is_final=1):
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
E.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)
Sullivan Expires 7 January 2027 [Page 112]
Internet-Draft raAE July 2026
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=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
Sullivan Expires 7 January 2027 [Page 113]
Internet-Draft raAE July 2026
E.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
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
Sullivan Expires 7 January 2027 [Page 114]
Internet-Draft raAE July 2026
ea17424dbcf70230e9f4bea73f5265a4
accumulator (32 octets):
176e02c54d994c99f50928a7c67386fb
ea17424dbcf70230e9f4bea73f5265a4
mask (32 octets):
5e9bb90f0671afe58fb72584dce62ae6
6bfaa33159a97eefc2de2304c8b2528a
wrapped_acc (32 octets):
49f5bbca4be8e37c7abe0d231a95ac1d
81ede17ce55e7cdf2b2a9da3f7e0372e
snapshot_tag (32 octets):
cb35b42e68a265b2c6a09de9b381044b
29f46e98342a44f6eaae3eb8f7470789
E.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
Sullivan Expires 7 January 2027 [Page 115]
Internet-Draft raAE July 2026
2fd60a1c388c54168621a40530ae4237
Segment 0 (is_final=1):
nonce (32 octets):
05050505050505050505050505050505
05050505050505050505050505050505
segment_aad (24 octets):
00095345414c2d444154410008000000
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
E.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
Sullivan Expires 7 January 2027 [Page 116]
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):
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
Sullivan Expires 7 January 2027 [Page 117]
Internet-Draft raAE July 2026
mask (32 octets):
ad55f19bb8997c8ad0def92830f0ce09
4542ec804f7b99e66f294164985979a8
wrapped_acc (32 octets):
eb2c57fe5481f00fbdc96f13df0d846e
e5afb57c161baac89a7c41dfa64b2641
snapshot_tag (32 octets):
a08c73b5c414542cc06830d893d0eaca
c749418dea32b11d5cc121d6b2db93b8
E.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
Sullivan Expires 7 January 2027 [Page 118]
Internet-Draft raAE July 2026
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):
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
E.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
Sullivan Expires 7 January 2027 [Page 119]
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): 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=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):
Sullivan Expires 7 January 2027 [Page 120]
Internet-Draft raAE July 2026
61c3caca0ecda929269f52978f4a60e1
contrib (32 octets):
85db0789dfd84dfaeb095fc28101d929
af58800d6ae8882353c83c9a6013492c
accumulator (32 octets):
171c8d53c6f72dce803c16db0770cd7d
31785723b4a3f08a05b4c88e56488ead
mask (32 octets):
e399bc51986eaf7f8d7cbcb3e76da43a
b9607c01e413dd462639c4fd50191f32
wrapped_acc (32 octets):
f48531025e9982b10d40aa68e01d6947
88182b2250b02dcc238d0c730651919f
snapshot_tag (32 octets):
f4bfbdff53178451463ef23c73a31ab5
06a6acfb49282af14598b2f49f32ceeb
E.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
Sullivan Expires 7 January 2027 [Page 121]
Internet-Draft raAE July 2026
3e3af8d2cbf8d40687d693243c32cdea
snap_key (32 octets):
aa42a852946818754780e48a9209a451
345367bd07a04ad794c62a703366aa90
Segment 0 (is_final=0):
nonce (12 octets):
030303030303030303030303
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
E.13. Two Segment, AEGIS-256, HKDF-SHA-256, 16384
Sullivan Expires 7 January 2027 [Page 122]
Internet-Draft raAE July 2026
Parameter set:
cipher suite:
aead_id 0x0021 (AEAD_AEGIS256)
kdf_id 0x0001 (HKDF-SHA-256)
geometry:
segment_max 16384
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=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):
Sullivan Expires 7 January 2027 [Page 123]
Internet-Draft raAE July 2026
nonce (32 octets):
09090909090909090909090909090909
09090909090909090909090909090909
segment_aad (24 octets):
00095345414c2d444154410008000000
0000000001000101
ciphertext (20 octets):
006704b222fa9285da01b706be831b76
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
E.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
Sullivan Expires 7 January 2027 [Page 124]
Internet-Draft raAE July 2026
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=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
Sullivan Expires 7 January 2027 [Page 125]
Internet-Draft raAE July 2026
mask (32 octets):
8d8832a7c865352b6ebcfabaa1c85c69
0aacfbfb2bf08a73cc090281bea1ca42
wrapped_acc (32 octets):
4b24ef79e0151bfd9f9f9dd5b3997e8b
c57df5122b9364fce87bb407b96b8e07
snapshot_tag (32 octets):
016300d12db61dc1b6b11fbeee8ecaf8
614efe5802f3fc919ab8aa665cdf9294
E.15. TurboSHAKE-256 Cipher Suite Vectors
These two vectors exercise the one-step TurboSHAKE-256 cipher suite
(Table 10), 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.
E.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):
Sullivan Expires 7 January 2027 [Page 126]
Internet-Draft raAE July 2026
bdecddf1340029c520b9b4a9e1b15144
d283209261a58113294728e337d14ea8
snap_key (64 octets):
92fc2e47ac72bbdbcac62a67ced07dad
a1e907bd82e92a68ba5f6098ec067931
e59683904d5213ddd0abe237ac0f9450
ef33180028f2ea7e47d738e6f3faed01
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
E.15.2. Two Segment, AES-256-GCM, TurboSHAKE-256, 65536
Sullivan Expires 7 January 2027 [Page 127]
Internet-Draft raAE July 2026
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
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):
Sullivan Expires 7 January 2027 [Page 128]
Internet-Draft raAE July 2026
97b151445a7f50b4f41811e8b9d0f4c9
04648ba6d13085e7d7180d94321aa3f8
4f895847053ce9c66e4b27f4251f6ffc
356e72ec1a867e2b405163732b65b8c1
Segment 1 (is_final=1):
nonce (12 octets):
070707070707070707070707
segment_aad (24 octets):
00095345414c2d444154410008000000
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
Sullivan Expires 7 January 2027 [Page 129]
Internet-Draft raAE July 2026
E.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.
E.16.1. Two Segment, AES-256-GCM, HKDF-SHA-256, 65536 (Rewrite)
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
Sullivan Expires 7 January 2027 [Page 130]
Internet-Draft raAE July 2026
0000000000000100
ciphertext (12 octets):
1815f12b13f7ee2532f0fcca
tag (16 octets):
2597e2f2243b98c4bb7f320dc2f46ce3
contrib (32 octets):
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
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):
Sullivan Expires 7 January 2027 [Page 131]
Internet-Draft raAE July 2026
d1799e445a84608aae7dfca35bc03813
138fd92f58adb6c1fec48907e9a90f9e
acc_delta (32 octets):
dcf2a2f66b1657f4265ed5e6ade32d18
0993d24e2cf7f962649bece68b1fe9ec
new_accumulator (32 octets):
7991412622d672f4abf8a4966662e6ea
890c4ba4143b59f65b1439878118a7e2
new_mask (32 octets):
76615f532f527eaecac7d2fa4232fc32
10596f72a1e2637cfbb5db656d53c9ac
new_wrapped_acc (32 octets):
0ff01e750d840c5a613f766c24501ad8
995524d6b5d93a8aa0a1e2e2ec4b6e4e
new_snapshot_tag (32 octets):
bba55e58311ebbd38d7880a9ebec3d19
3212c0600ce04aeeb7c18ee62e33b9cc
E.17. Derived-Nonce Cipher Suite Vector
This vector exercises AES-256-GCM-SIV (Table 9), 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.
E.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
Sullivan Expires 7 January 2027 [Page 132]
Internet-Draft raAE July 2026
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
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):
Sullivan Expires 7 January 2027 [Page 133]
Internet-Draft raAE July 2026
3392acaffbaaa0224644ee4b0efa53ad
c0d21628
tag (16 octets):
408046d4fb0789a8ae0c41ddb0f66fc3
contrib (32 octets):
7e2e00b404bf5962b6220c4196298f4b
d25bff62bd9e3230ddd0d344e2b78cc1
accumulator (32 octets):
82caee66d037e88c3fb6ce315b3ddd49
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):
Sullivan Expires 7 January 2027 [Page 134]
Internet-Draft raAE July 2026
a4ad2d7fa8abbbec998af5c3ced3514f
e420923c403ba57771bad99a8d03034a
E.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.
E.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):
Sullivan Expires 7 January 2027 [Page 135]
Internet-Draft raAE July 2026
nonce (12 octets):
030303030303030303030303
epoch_key (32 octets):
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
Sullivan Expires 7 January 2027 [Page 136]
Internet-Draft raAE July 2026
snapshot_tag (32 octets):
7ec104576a2197294a35d2d28000331e
30dc4172f1f18492950e15ef0c1f1742
E.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 24, 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.
E.19.1. AES-256-GCM, HKDF-SHA-256, 16384, Segment 0
The payload schedule is that of Appendix E.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
Sullivan Expires 7 January 2027 [Page 137]
Internet-Draft raAE July 2026
E.19.2. AES-256-GCM, TurboSHAKE-256, 65536, Segment 1
The payload schedule is that of the single-segment TurboSHAKE-256
block (Appendix E.15). The segment index is 1, visible in the second
framed element of nonce_ctx.
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
E.20. 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.
E.20.1. Tampered Accumulator, Snapshot Tag Not Recomputed
Sullivan Expires 7 January 2027 [Page 138]
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
Appendix F. Changes from draft-sullivan-cfrg-raae-00
This appendix is informative. It summarizes the substantive changes
from the -00 revision.
* SEAL: the -00 monolithic raAE-v1 profile became SEAL, a
parameterized construction with two named profiles, SEAL-RW-v1
(mutable) and SEAL-RO-v1 (write-once). payload_info gained
snap_id (the snapshot authenticator identifier) and dropped
aad_label, segment_max is a power of two of at least 4096 octets,
and the five named instantiations fix complete parameter sets and
a serialization layout (Section 4.12).
* Structural split: the raAE primitive (Section 3), the SEAL
construction that realizes it (Section 4), and the cipher suites
(Section 4.10) are now specified separately.
* Snapshot authenticator (Section 4.7): -00 published the bare XOR
accumulator as its whole-object check. That design is superseded
for a security reason: a write adversary can recombine observed
accumulator differences into a non-historical segment set that
still verifies (Appendix C.5), so the -00 snapshot offers no
whole-object integrity against rewrites. The replacement is the
masked multiset hash: a snapshot tag MACs the count and
Sullivan Expires 7 January 2027 [Page 139]
Internet-Draft raAE July 2026
accumulator, and a deterministic, tag-derived mask hides the
accumulator, so the published value is wrapped_acc ||
snapshot_tag.
* Wire format, code points: aead_id and kdf_id are now 2-octet IANA
code points.
* Wire format, salt: the per-content salt is now a fixed 32 octets.
* Wire format, KDF output size: TurboSHAKE-256 uses Nh = 64.
* KDF framing: the KDF framing function is total, and the one-step
form frames ikm and info each as one element.
* Security analysis: reorganized to be property-oriented. The body
states properties, assumptions, requirements, and operational
limits. The reductions, bounds, and proof symbols are in
Appendix C, and the formal proofs are deferred to a companion
paper in preparation.
* Integrity requirements: unauthorized truncation detection is a
normative decryptor requirement, n_seg = 0 is disallowed without a
snapshot authenticator, snapshot verification is required when all
segment tags are available, commitment_length is at least 16
octets, and the snapshot comparison is constant-time.
* SEAL-compact named instantiation: added SEAL-compact (SEAL-RW-v1,
derived nonce, aligned layout), naming an already-buildable
combination that gives large random-access objects compact per-
segment metadata. Byte-neutral.
* The wire-format and snapshot changes above are reflected in a
regenerated test-vector corpus (Appendix E).
* Normative tightenings from implementation evidence: epoch-key
derivation applies at every epoch_length and payload_key is never
a segment key (Section 4.5.2), a SEAL-RO-v1 encryptor MUST NOT
rewrite a written segment (Section 4.10.2), streamed plaintext is
unverified for completeness until the terminal finality check
(Section 4.9.1.2), and the SnapVerify comparison covers the full
snapshot value in one constant-time comparison (Section 6.9).
* Presentation, from the same evidence: the plaintext-bound
construction's labels are tabulated adjacent to the construction
(Appendix A), the profiles state where truncation detection
surfaces, and the layout size formulas carry Nt symbolically.
Sullivan Expires 7 January 2027 [Page 140]
Internet-Draft raAE July 2026
* Test vectors, from the same evidence: component vectors for the
plaintext-bound nonce construction (Appendix E.19), a worked byte-
layout example for the one-step KDF (Section 4.3.2), and
regenerated TurboSHAKE-256 cipher-suite vectors matching the
nested one-step framing this document specifies.
* Terminology: the interface extension is named the extended raAE
interface throughout, replacing the earlier "mutable raAE"
phrasing, so no text suggests two primitives named raAE. Mutable
and immutable now describe only the SEAL profiles' write
discipline.
* Attribution: the introduction credits the base interface and
security notions to Fábrega et al., related work names FLOE as the
base-interface construction of that work, and the extension
section states which interface tier each construction realizes.
* Global associated data: G is a per-message input, the StartEnc G
of the primitive, always the last element of the commitment
derivation and empty by default. It is never stored. Every
commitment value changes, and the test-vector corpus is
regenerated. Appendix E.2 pins the default and a nonempty G.
Acknowledgments
The author thanks Andrés Fábrega, Thomas Ristenpart, Gregory Rubin,
Richard Barnes, Thibault Meunier, Kenny Paterson, Christopher Patton,
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 7 January 2027 [Page 141]