<?xml version='1.0' encoding='utf-8'?>
<!DOCTYPE rfc [
  <!ENTITY nbsp    "&#160;">
  <!ENTITY zwsp   "&#8203;">
  <!ENTITY nbhy   "&#8209;">
  <!ENTITY wj     "&#8288;">
]>
<?xml-stylesheet type="text/xsl" href="rfc2629.xslt" ?>
<!-- generated by https://github.com/cabo/kramdown-rfc version 1.7.21 (Ruby 3.3.6) -->
<rfc xmlns:xi="http://www.w3.org/2001/XInclude" ipr="trust200902" docName="draft-mouris-cfrg-mastic-04" category="info" consensus="true" submissionType="IRTF" tocInclude="true" sortRefs="true" symRefs="true" version="3">
  <!-- xml2rfc v2v3 conversion 3.25.0 -->
  <front>
    <title abbrev="Mastic">The Mastic VDAF</title>
    <seriesInfo name="Internet-Draft" value="draft-mouris-cfrg-mastic-04"/>
    <author initials="H." surname="Davis" fullname="Hannah Davis">
      <organization>Seagate</organization>
      <address>
        <email>hannah.e.davis@seagate.com</email>
      </address>
    </author>
    <author initials="D." surname="Mouris" fullname="Dimitris Mouris">
      <organization>Nillion</organization>
      <address>
        <email>dimitris@nillion.com</email>
      </address>
    </author>
    <author initials="C." surname="Patton" fullname="Christopher Patton">
      <organization>Cloudflare</organization>
      <address>
        <email>chrispatton+ietf@gmail.com</email>
      </address>
    </author>
    <author initials="N. G." surname="Tsoutsos" fullname="Nektarios G. Tsoutsos">
      <organization>University of Delaware</organization>
      <address>
        <email>tsoutsos@udel.edu</email>
      </address>
    </author>
    <date year="2025" month="January" day="24"/>
    <area>IRTF</area>
    <workgroup>Crypto Forum</workgroup>
    <keyword>Internet-Draft</keyword>
    <abstract>
      <?line 119?>

<t>This document describes Mastic, a two-party VDAF for the following secure
aggregation task: each client holds an input string and an associated weight,
and the data collector wants to aggregate the weights of all clients whose
inputs begin with a prefix chosen by the data collector. This functionality
enables two classes of applications. First, it allows grouping metrics by
client attributes without revealing which clients have which attributes.
Second, it solves the weighted heavy hitters problem, where the goal is to
compute the subset of inputs that have the highest total weight.</t>
    </abstract>
    <note removeInRFC="true">
      <name>About This Document</name>
      <t>
        Status information for this document may be found at <eref target="https://datatracker.ietf.org/doc/draft-mouris-cfrg-mastic/"/>.
      </t>
      <t>
        Discussion of this document takes place on the
        Crypto Forum Research Group mailing list (<eref target="mailto:cfrg@ietf.org"/>),
        which is archived at <eref target="https://mailarchive.ietf.org/arch/search/?email_list=cfrg"/>.
        Subscribe at <eref target="https://www.ietf.org/mailman/listinfo/cfrg/"/>.
      </t>
      <t>Source for this draft and an issue tracker can be found at
        <eref target="https://github.com/jimouris/draft-mouris-cfrg-mastic"/>.</t>
    </note>
  </front>
  <middle>
    <?line 130?>

<section anchor="introduction">
      <name>Introduction</name>
      <t>(RFC EDITOR: Remove this paragraph.) The source for this draft and the
reference code can be found at
https://github.com/jimouris/draft-mouris-cfrg-mastic.</t>
      <t>The private "heavy hitters" problem is to compute the most popular input
strings generated by clients without learning the inputs themselves. For
example, a browser vendor might want to know which websites are visited most
frequently without learning which clients visited which websites.</t>
      <t>This problem can be solved by combining a binary search with a subroutine
solving the simpler "prefix histogram" problem. The goal of this problem is to
count how many of the input strings begin with each of a sequence of candidate
prefixes. This problem can be solved using a Verifiable Distributed Aggregation
Function, or VDAF <xref target="VDAF"/>.</t>
      <t>The Poplar1 VDAF specified in <xref section="8" sectionFormat="of" target="VDAF"/> describes how to
distribute this computation amongst two aggregation servers such that, as long
as one server is honest, no individual's input is observed in the clear. At the
same time, Poplar1 allows the servers to detect and remove any invalid
measurements that would otherwise corrupt the computation.</t>
      <t>This document describes Mastic <xref target="MPDST25"/>, a VDAF for the following, more
general functionality: each client holds an input and an associated weight, and
the data collector's goal is, for each candidate prefix, to aggregate the
weights of all clients whose inputs have the prefix in common. This
functionality gives rise to two types of applications:</t>
      <ol spacing="normal" type="1"><li>
          <t>"weighted heavy hitters": Rather than compute the most frequent inputs, as
in plain heavy hitters, the goal here is to compute the set of inputs with
the highest total weight. For example, a browser vendor might want to know
which web pages have the highest average load time, perhaps indicating a
performance issue in the browser. Because weighted heavy hitters is more
general, Mastic can be used as a drop-in replacement for Poplar1. It is is
also more efficient, requiring just one round of communication for
preparation (<xref section="5.2" sectionFormat="of" target="VDAF"/>) compared to Poplar1's two rounds.</t>
        </li>
        <li>
          <t>"attribute-based metrics": The Prio3 VDAF (<xref section="7" sectionFormat="of" target="VDAF"/>) can be
used for a variety of aggregation tasks, ranging from simple summary
statistics, like average or standard deviation, to more sophisticated
representations of data, like histograms or linear regression. In many
situations, it is desirable to group such metrics by client attributes such
as geolocation or user agent (<xref section="10.1.5" sectionFormat="of" target="RFC9110"/>). Mastic
provides this functionality without revealing any client's attribute to the
aggregation servers or data collector.</t>
        </li>
      </ol>
      <t>The main component of Mastic is the Verifiable Incremental Distributed Point
Function (VIDPF) of <xref target="MST24"/>. VIDPF extends IDPF (<xref section="8.3" sectionFormat="of" target="VDAF"/>),
the main building block of Poplar1. Both IDPF and VIDPF are a form of function
secret sharing <xref target="BGI15"/>, where a client generates shares of a secret function
<tt>F</tt> such that each server can compute shares of <tt>F(X)</tt> for a chosen <tt>X</tt>. In our
case, the function being shared is associated with a secret input string <tt>alpha</tt>
and weight <tt>beta</tt> for which <tt>F(X) = beta</tt> for every prefix <tt>X</tt> of <tt>alpha</tt> and
<tt>F(X) = 0</tt> for every <tt>X</tt> this is not a prefix of <tt>alpha</tt>. The scheme is
verifiable in the sense that, for any two candidate prefixes of the same length,
the servers can verify that at most one of them evaluates to <tt>beta</tt> and the
other(s) evaluate(s) to <tt>0</tt>.</t>
      <t>Mastic combines VIDPF with a method for checking that <tt>beta</tt> itself is a valid
weight. For example, if the weights represent page load times, it is important
to make sure each weight is within a sensible range, say within seconds rather
than hours or days. Otherwise, misbehaving clients would be able to poison the
computation by reporting out of range values.</t>
      <t>This range check is accomplished with the Fully Linear Proof (FLP) system of
<xref section="7.3" sectionFormat="of" target="VDAF"/>. An FLP allows properties of secret shared data to be
validated without revealing the data itself. In Mastic, the client generates an
FLP of its <tt>beta</tt>'s validity; when the servers are ready to evaluate the VIDPF,
they first compute shares of <tt>beta</tt> and verify the FLP, which itself is secret
shared. Then the VIDPF ensures that the non-<tt>0</tt> output of the point function is
the same for each evaluation.</t>
      <t>This document specifies VIDPF in <xref target="vidpf"/> and the composition of VIDPF and FLP
into Mastic in <xref target="vidpf"/>. The appendix includes supplementary material:</t>
      <ul spacing="normal">
        <li>
          <t><xref target="motivation"/> discusses some use cases that motivated Mastic's
functionality.</t>
        </li>
        <li>
          <t><xref target="additional-modes"/> describes extensions and optimizations for Mastic,
including a batched "preparation" (<xref section="5.2" sectionFormat="of" target="VDAF"/>) mode of
operation that reduces communication cost, and a 3-party variant of the
protocol that ensures robustness against poisoning attacks in the presence of
one malicious aggregation server.</t>
        </li>
      </ul>
    </section>
    <section anchor="conventions">
      <name>Conventions and Definitions</name>
      <t>The key words "<bcp14>MUST</bcp14>", "<bcp14>MUST NOT</bcp14>", "<bcp14>REQUIRED</bcp14>", "<bcp14>SHALL</bcp14>", "<bcp14>SHALL
NOT</bcp14>", "<bcp14>SHOULD</bcp14>", "<bcp14>SHOULD NOT</bcp14>", "<bcp14>RECOMMENDED</bcp14>", "<bcp14>NOT RECOMMENDED</bcp14>",
"<bcp14>MAY</bcp14>", and "<bcp14>OPTIONAL</bcp14>" in this document are to be interpreted as
described in BCP 14 <xref target="RFC2119"/> <xref target="RFC8174"/> when, and only when, they
appear in all capitals, as shown here.</t>
      <?line -18?>

<t>This document uses the same conventions and definitions as <xref section="2" sectionFormat="of" target="VDAF"/>. The following terms as defined in therein:
"Aggregator",
"Client",
"Collector",
"aggregate result",
"aggregate share",
"aggregation parameter",
"batch",
"input share",
"measurement",
"output share",
"prep message",
"prep share", and
"report".</t>
      <t>The following functions are as defined therein:</t>
      <table anchor="common-functions">
        <name>Common Functionalities.</name>
        <thead>
          <tr>
            <th align="left">Functionality</th>
            <th align="left">Type</th>
            <th align="left">Definition</th>
          </tr>
        </thead>
        <tbody>
          <tr>
            <td align="left">
              <tt>byte</tt></td>
            <td align="left">Function</td>
            <td align="left">Section 2</td>
          </tr>
          <tr>
            <td align="left">
              <tt>cast</tt></td>
            <td align="left">Function</td>
            <td align="left">Section 2</td>
          </tr>
          <tr>
            <td align="left">
              <tt>concat</tt></td>
            <td align="left">Function</td>
            <td align="left">Section 2</td>
          </tr>
          <tr>
            <td align="left">
              <tt>front</tt></td>
            <td align="left">Function</td>
            <td align="left">Section 2</td>
          </tr>
          <tr>
            <td align="left">
              <tt>range</tt></td>
            <td align="left">Function</td>
            <td align="left">Section 2</td>
          </tr>
          <tr>
            <td align="left">
              <tt>to_le_bytes</tt></td>
            <td align="left">Function</td>
            <td align="left">Section 2</td>
          </tr>
          <tr>
            <td align="left">
              <tt>xor</tt></td>
            <td align="left">Function</td>
            <td align="left">Section 2</td>
          </tr>
        </tbody>
      </table>
      <t>Mastic also uses finite fields as specified in <xref section="6.1" sectionFormat="of" target="VDAF"/>. We
usually denote a finite field by <tt>F</tt> and its Python class object, a subclass of
<tt>Field</tt>, as <tt>field: type[F]</tt>. This document references the following operations
on fields, defined in <xref section="6.1" sectionFormat="of" target="VDAF"/>:</t>
      <table anchor="field-functionalities">
        <name>Finite Field Functionalities.</name>
        <thead>
          <tr>
            <th align="left">Functionality</th>
            <th align="left">Type</th>
            <th align="left">Definition</th>
          </tr>
        </thead>
        <tbody>
          <tr>
            <td align="left">
              <tt>Field</tt></td>
            <td align="left">Constructor</td>
            <td align="left">Section 6.1</td>
          </tr>
          <tr>
            <td align="left">
              <tt>field.encode_vec</tt></td>
            <td align="left">Instance Method</td>
            <td align="left">Section 6.1.1</td>
          </tr>
          <tr>
            <td align="left">
              <tt>field.zeros</tt></td>
            <td align="left">Instance Method</td>
            <td align="left">Section 6.1</td>
          </tr>
          <tr>
            <td align="left">
              <tt>vec_add</tt></td>
            <td align="left">Function</td>
            <td align="left">Section 6.1.1</td>
          </tr>
          <tr>
            <td align="left">
              <tt>vec_neg</tt></td>
            <td align="left">Function</td>
            <td align="left">Section 6.1.1</td>
          </tr>
          <tr>
            <td align="left">
              <tt>vec_sub</tt></td>
            <td align="left">Function</td>
            <td align="left">Section 6.1.1</td>
          </tr>
        </tbody>
      </table>
      <t>Mastic uses the Fully Linear Proof (FLP) system specified in <xref section="7.3" sectionFormat="of" target="VDAF"/>. The draft refers to the following methods on an instance <tt>flp</tt> of the
class <tt>Flp</tt> defined in <xref target="VDAF"/>:</t>
      <table anchor="FLP-functionalities">
        <name>FLP methods and parameters.</name>
        <thead>
          <tr>
            <th align="left">Functionality</th>
            <th align="left">Type  --</th>
            <th align="left">Definition</th>
          </tr>
        </thead>
        <tbody>
          <tr>
            <td align="left">
              <tt>flp.decide</tt></td>
            <td align="left">Instance Method</td>
            <td align="left">Section 7.1</td>
          </tr>
          <tr>
            <td align="left">
              <tt>flp.decode</tt></td>
            <td align="left">Instance Method</td>
            <td align="left">Section 7.1.1</td>
          </tr>
          <tr>
            <td align="left">
              <tt>flp.encode</tt></td>
            <td align="left">Instance Method</td>
            <td align="left">Section 7.1.1</td>
          </tr>
          <tr>
            <td align="left">
              <tt>flp.prove</tt></td>
            <td align="left">Instance Method</td>
            <td align="left">Section 7.1</td>
          </tr>
          <tr>
            <td align="left">
              <tt>flp.query</tt></td>
            <td align="left">Instance Method</td>
            <td align="left">Section 7.1</td>
          </tr>
          <tr>
            <td align="left">
              <tt>flp.truncate</tt></td>
            <td align="left">Instance Method</td>
            <td align="left">Section 7.1.1</td>
          </tr>
          <tr>
            <td align="left">
              <tt>MEAS_LEN</tt></td>
            <td align="left">integer</td>
            <td align="left">Section 7.3.2</td>
          </tr>
          <tr>
            <td align="left">
              <tt>OUTPUT_LEN</tt></td>
            <td align="left">integer</td>
            <td align="left">Section 7.3.2</td>
          </tr>
        </tbody>
      </table>
      <t>Mastic also uses eXtendable Output Functions (XOFs) as specified in <xref section="6.2" sectionFormat="of" target="VDAF"/>. The following functionalities are as defined therein (<tt>xof</tt>
denotes an instance of class <tt>Xof</tt>):</t>
      <table anchor="XOF-functionalities">
        <name>XOF Functionalities.</name>
        <thead>
          <tr>
            <th align="left">Functionality</th>
            <th align="left">Type</th>
            <th align="left">Definition</th>
          </tr>
        </thead>
        <tbody>
          <tr>
            <td align="left">
              <tt>XofFixedKeyAes128</tt></td>
            <td align="left">Constructor</td>
            <td align="left">Section 6.2</td>
          </tr>
          <tr>
            <td align="left">
              <tt>XofTurboShake128</tt></td>
            <td align="left">Constructor</td>
            <td align="left">Section 6.2</td>
          </tr>
          <tr>
            <td align="left">
              <tt>xof.next</tt></td>
            <td align="left">Instance Method</td>
            <td align="left">Section 6.2</td>
          </tr>
        </tbody>
      </table>
      <t>Each invocation of a XOF is initialized with a domain separation tag. Each
domain separation tag encodes the version of this document, the application
context string (<xref section="4.1" sectionFormat="of" target="VDAF"/>), and a distinguished byte identifying
how the XOF output is used. The tag may also encode a VDAF algorithm ID
(<xref section="5" sectionFormat="of" target="VDAF"/>).</t>
      <t>The version of this document is a byte denoted <tt>VERSION</tt>. Its value <bcp14>SHALL</bcp14> be <tt>0</tt>.</t>
      <ul empty="true">
        <li>
          <t>NOTE We'll bump <tt>VERSION</tt> whenever we publish a draft with incompatible
changes from the previous draft.</t>
        </li>
      </ul>
      <t>Algorithms in the remainder will use the following algorithms:</t>
      <sourcecode type="python"><![CDATA[
def dst(ctx: bytes, usage: int) -> bytes:
    return b'mastic' + byte(VERSION) + byte(usage) + ctx

def dst_alg(ctx: bytes, usage: int, algorithm_id: int) -> bytes:
    return b'mastic'\
        + byte(VERSION) \
        + byte(usage) \
        + to_be_bytes(algorithm_id, 4) \
        + ctx
]]></sourcecode>
      <t>When using Mastic or VIDPF, the length of the application context string
(denoted <tt>ctx</tt>) <bcp14>MUST</bcp14> be in range <tt>[0, 2^16 - 12)</tt>.</t>
      <ul empty="true">
        <li>
          <t>NOTE This range was computed by taking the maximum size of the domain
separation tag supported by both XofFixedKeyAes128 and XofTurboShake128 and
subtracting the length of the prefix.</t>
        </li>
      </ul>
      <t>Finally, for completeness, we define some Python methods used in the remainder:</t>
      <ul spacing="normal">
        <li>
          <t><tt>bool(val: Any) -&gt; bool</tt> converts a value <tt>val</tt> to a Boolean.</t>
        </li>
        <li>
          <t><tt>bytearray(source: Union[str, bytes, bytearray, Iterable[int]]) -&gt;
bytearray</tt> returns a mutable sequence of bytes.</t>
        </li>
        <li>
          <t><tt>bytes(source: Union[str, bytes, bytearray, Iterable[int]]) -&gt; bytes</tt>
returns a immutable sequence of bytes.</t>
        </li>
        <li>
          <t><tt>len(obj: Sized) -&gt; int</tt> returns the number of items in <tt>obj</tt>. The object
argument can be a sequence (e.g., string, list, or tuple) or a collection
(e.g., dictionary, set).</t>
        </li>
        <li>
          <t><tt>list.append(elem: Any) -&gt; None</tt> adds a single element <tt>elem</tt> to the end of
a <tt>list</tt> instance.</t>
        </li>
        <li>
          <t><tt>list.copy() -&gt; list</tt> returns a shallow copy of a <tt>list</tt> instance.</t>
        </li>
        <li>
          <t><tt>set(iterable: Optional[Iterable] = None) -&gt; set</tt> creates a new set object,
which is an unordered collection of unique elements. The optional <tt>iterable</tt>
argument (e.g., list, tuple, or string) is used to initialize the set. If no
argument is provided, an empty set is created.</t>
        </li>
      </ul>
    </section>
    <section anchor="vidpf">
      <name>Specification of VIDPF</name>
      <table anchor="vidpf-params">
        <name>VIDPF parameters.</name>
        <thead>
          <tr>
            <th align="left">Parameter</th>
            <th align="left">Description</th>
            <th align="left">Value</th>
          </tr>
        </thead>
        <tbody>
          <tr>
            <td align="left">
              <tt>KEY_SIZE: int</tt></td>
            <td align="left">the size of each VIDPF key</td>
            <td align="left">
              <tt>XofFixedKeyAes128.SEED_SIZE</tt> (<xref section="6.2.2" sectionFormat="of" target="VDAF"/>)</td>
          </tr>
          <tr>
            <td align="left">
              <tt>NONCE_SIZE: int</tt></td>
            <td align="left">the size of the VIDPF nonce</td>
            <td align="left">
              <tt>KEY_SIZE</tt></td>
          </tr>
          <tr>
            <td align="left">
              <tt>RAND_SIZE: int</tt></td>
            <td align="left">the number of random bytes consumed by <tt>gen()</tt></td>
            <td align="left">
              <tt>2 * KEY_SIZE</tt></td>
          </tr>
          <tr>
            <td align="left">
              <tt>BITS: int</tt></td>
            <td align="left">bit length of the input string <tt>alpha</tt></td>
            <td align="left">set by constructor</td>
          </tr>
          <tr>
            <td align="left">
              <tt>VALUE_LEN: int</tt></td>
            <td align="left">length of <tt>beta</tt></td>
            <td align="left">set by constructor</td>
          </tr>
          <tr>
            <td align="left">
              <tt>field: type[F]</tt></td>
            <td align="left">class object for the field (<xref section="6.1" sectionFormat="of" target="VDAF"/>)</td>
            <td align="left">set by constructor</td>
          </tr>
        </tbody>
      </table>
      <ul empty="true">
        <li>
          <t>NOTE This specification is based on <xref target="MST24"/>, which in turn draws on ideas
from <xref target="CP22"/>. We don't yet have a concrete security analysis of the complete
construction. Some details are likely to change as a result of such analysis.</t>
        </li>
      </ul>
      <t>This section specifies the Verifiable Incremental Distributed Point Function
(VIDPF) of <xref target="MST24"/>. Its parameters are summarized in <xref target="vidpf-params"/>.</t>
      <t>VIDPF is a function secret sharing scheme <xref target="BGI15"/> for functions <tt>F</tt> for which:</t>
      <ul spacing="normal">
        <li>
          <t><tt>F(X) = </tt>beta<tt> if </tt>X<tt> is a prefix of </tt>alpha`</t>
        </li>
        <li>
          <t><tt>F(X) = field.zeros(VALUE_LEN)</tt> if <tt>x</tt> is not a prefix of <tt>alpha</tt></t>
        </li>
      </ul>
      <t>where <tt>alpha</tt> and <tt>beta</tt> are the input and encoded weight of a Client and
<tt>field</tt> the finite field. The scheme is designed to allow each Aggregator to
compute a share of <tt>F(X)</tt> for any candidate prefix <tt>X</tt> without revealing any
information about <tt>alpha</tt> or <tt>beta</tt>. Furthermore, the output shares can be
aggregated locally, allowing each Aggregator to compute a share of the total
weight for all inputs that have <tt>X</tt> as a prefix.</t>
      <t>VIDPF comprises two algorithms.</t>
      <t>The key generation algorithm defined in <xref target="vidpf-key-gen"/> takes in a <tt>(alpha,
beta)</tt> and the report nonce and outputs secret shares of <tt>F</tt>. The shares take
the form of a pair of "keys", one for each Aggregator, and a sequence of
"correction words" sent to both Aggregators. We define correction words in the
next section.</t>
      <t>The Aggregators evaluate a Client's VIDPF on a sequence of candidate prefixes.
Imagine arranging these prefixes in a binary tree where each path from the root
corresponds to a prefix <tt>X</tt> and each node corresponds to a payload <tt>F(X)</tt>. We
refer to this as the "prefix tree".</t>
      <t>The key evaluation algorithm defined in <xref target="vidpf-key-eval"/> takes in the
correction words, the Aggregator's key, the sequence of candidate prefixes, and
the nonce associated with the Client's report. It the Aggregator's share of the
prefix tree.</t>
      <ul empty="true">
        <li>
          <t>TODO Define the "node proofs".</t>
        </li>
      </ul>
      <section anchor="vidpf-key-gen">
        <name>Key Generation</name>
        <t>The VIDPF-key generation algorithm run by each Client is listed below. The
specification invokes auxiliary functions, namely <tt>extend()</tt>, <tt>convert()</tt>, and
<tt>node_proof()</tt>, which are defined in <xref target="vidpf-aux"/>. Its inputs are the input
string <tt>alpha</tt>, the encoded weight <tt>beta</tt>, the application context string <tt>ctx</tt>
(<xref section="4.1" sectionFormat="of" target="VDAF"/>), a public nonce of length <tt>NONCE_SIZE</tt>, and the
randomness consumed by the algorithm of length <tt>RAND_SIZE</tt>. Its outputs are the
public sequence of "correction words", one for each level of the tree, and the
secret keys, one for each Aggregator.</t>
        <ul empty="true">
          <li>
            <t>TODO Give a high level overview of how IDPF works, in particular the
seed/control-bit invariant for each level. Define <tt>CorrectionWord</tt> and
explain the role of correction words and define node proofs, which are unique
to VIDPF.</t>
            <t>TODO Specify bounds on the inputs, namely that the nonce has to be random.
(This is inherited from IDPF security considerations.)</t>
            <t>TODO Explain functional differences between VIDPF and IDPF in
<xref section="8" sectionFormat="of" target="VDAF"/>. Namely, there is no distinction between inner and
leaf nodes and the payload is supposed to be the same at each level.</t>
          </li>
        </ul>
        <sourcecode type="python"><![CDATA[
def gen(self,
        alpha: tuple[bool, ...],
        beta: list[F],
        ctx: bytes,
        nonce: bytes,
        rand: bytes,
        ) -> tuple[list[CorrectionWord], list[bytes]]:
    '''
    The VIDPF key generation algorithm.

    Returns the public share (i.e., the correction word for each
    level of the tree) and two keys, one for each aggregator.

    Implementation note: for clarity, this algorithm has not been
    written in a manner that is side-channel resistant. To avoid
    leaking `alpha` via a side-channel, implementations should avoid
    branching or indexing into arrays in a data-dependent manner.
    '''
    if len(alpha) != self.BITS:
        raise ValueError("alpha out of range")
    if len(beta) != self.VALUE_LEN:
        raise ValueError("incorrect beta length")
    if len(nonce) != self.NONCE_SIZE:
        raise ValueError("incorrect nonce size")
    if len(rand) != self.RAND_SIZE:
        raise ValueError("randomness has incorrect length")

    keys = [rand[:self.KEY_SIZE], rand[self.KEY_SIZE:]]

    # [MST24, Fig. 15]: s0^0, s1^0, t0^0, t1^0
    seed = keys.copy()
    ctrl = [False, True]
    correction_words = []
    for i in range(self.BITS):
        idx = PrefixTreeIndex(alpha[:i+1])
        bit = alpha[i]

        # [MST24]: if x = 0 then keep <- L, lose <- R
        #
        # Implementation note: the value of `bit` is
        # `alpha`-dependent.
        (keep, lose) = (1, 0) if bit else (0, 1)

        # Extend: compute the left and right children the current
        # level of the tree. During evaluation, one of these children
        # will be selected as the next seed and control bit.
        #
        # [MST24]: s_0^L || s_0^R || t_0^L || t_0^R
        #          s_1^L || s_1^R || t_1^L || t_1^R
        (s0, t0) = self.extend(seed[0], ctx, nonce)
        (s1, t1) = self.extend(seed[1], ctx, nonce)

        # Compute the correction words for this level's seed and
        # control bit. Our goal is to maintain the following
        # invariant, after correction:
        #
        # * If evaluation is on path, then each aggregator will
        #   compute a different seed and their control bits will be
        #   secret shares of one.
        #
        # * If evaluation is off path, then the aggregators will
        #   compute the same seed and their control bits will be
        #   shares of zero.
        #
        # Implementation note: the index `lose` is `alpha`-dependent.
        seed_cw = xor(s0[lose], s1[lose])
        ctrl_cw = [
            t0[0] ^ t1[0] ^ (not bit),  # [MST24]: t_c^L
            t0[1] ^ t1[1] ^ bit,        # [MST24]: t_c^R
        ]

        # Correct.
        #
        # Implementation note: the index `keep` is `alpha`-dependent,
        # as is `ctrl`.
        if ctrl[0]:
            s0[keep] = xor(s0[keep], seed_cw)
            t0[keep] ^= ctrl_cw[keep]
        if ctrl[1]:
            s1[keep] = xor(s1[keep], seed_cw)
            t1[keep] ^= ctrl_cw[keep]

        # Convert.
        (seed[0], w0) = self.convert(s0[keep], ctx, nonce)
        (seed[1], w1) = self.convert(s1[keep], ctx, nonce)
        ctrl[0] = t0[keep]  # [MST24]: t0'
        ctrl[1] = t1[keep]  # [MST24]: t1'

        # Compute the correction word for this level's payload.
        #
        # Implementation note: `ctrl` is `alpha`-dependent.
        w_cw = vec_add(vec_sub(beta, w0), w1)
        if ctrl[1]:
            w_cw = vec_neg(w_cw)

        # Compute the correction word for this level's node proof. If
        # evaluation is on path, then exactly one of the aggregatos
        # will correct their node proof, causing them to compute the
        # same node value. If evaluation is off path, then both will
        # correct or neither will; and since they compute the same
        # seed, they will again compute the same value.
        proof_cw = xor(
            self.node_proof(seed[0], ctx, idx),
            self.node_proof(seed[1], ctx, idx),
        )

        correction_words.append((seed_cw, ctrl_cw, w_cw, proof_cw))

    return (correction_words, keys)
]]></sourcecode>
      </section>
      <section anchor="vidpf-key-eval">
        <name>Key Evaluation</name>
        <t>The VIDPF-key evaluation algorithm is listed below. See <xref target="vidpf-aux"/> for
deferred auxiliary functions. Its inputs are the Aggregator's ID (either <tt>0</tt> or
<tt>1</tt>), the correction words, the Aggregator's key, the level of the tree, the
sequence of prefixes, and the nonce associated with the report. Its outputs are
the sequence of output shares for each prefix, and the evaluation proof.</t>
        <ul empty="true">
          <li>
            <t>TODO Define <tt>PrefixTreeIndex</tt> and <tt>PrefixTreeEntry</tt>.</t>
            <t>TODO Say why we also visit the siblings of nodes in the prefix tree (it's
needed for Mastic).</t>
          </li>
        </ul>
        <sourcecode type="python"><![CDATA[
def eval_next(self,
                node: PrefixTreeEntry,
                correction_word: CorrectionWord,
                ctx: bytes,
                nonce: bytes,
                idx: PrefixTreeIndex,
                ) -> PrefixTreeEntry:
    """
    Extend a node in the tree, select and correct one of its
    children, then convert it into a payload and the next seed.
    """
    (seed_cw, ctrl_cw, w_cw, proof_cw) = correction_word
    keep = int(idx.path[-1])

    # Extend.
    #
    # [MST24, Fig. 17]: (s^L, s^R), (t^L, t^R) = PRG(s^{i-1})
    (s, t) = self.extend(node.seed, ctx, nonce)

    # Correct.
    #
    # Implementation note: avoid branching on the value of control
    # bits, as its value may be leaked by a side channel.
    if node.ctrl:
        s[keep] = xor(s[keep], seed_cw)
        t[keep] ^= ctrl_cw[keep]

    # Convert and correct the payload.
    #
    # Implementation note: the conditional addition should be
    # replaced with a constant-time select in practice in order to
    # reduce leakage via timing side channels.
    (next_seed, w) = self.convert(s[keep], ctx, nonce)
    next_ctrl = t[keep]  # [MST24]: s^i, W^i, t'^i
    if next_ctrl:
        w = vec_add(w, w_cw)

    # Compute and correct the node proof.
    #
    # Implementation note: avoid branching on the control bit here.
    node_proof = self.node_proof(next_seed, ctx, idx)
    if next_ctrl:
        node_proof = xor(node_proof, proof_cw)

    return PrefixTreeEntry(next_seed, next_ctrl, w, node_proof)
]]></sourcecode>
        <t>Evaluating the prefix tree, plus the sibling of each n ode in the prefix tree:</t>
        <sourcecode type="python"><![CDATA[
def eval_with_siblings(self,
                        agg_id: int,
                        correction_words: list[CorrectionWord],
                        key: bytes,
                        level: int,
                        prefixes: tuple[tuple[bool, ...], ...],
                        ctx: bytes,
                        nonce: bytes,
                        ) -> tuple[list[list[F]], PrefixTreeEntry]:
    """
    The VIDPF key evaluation algorithm.

    The return value consists of the weights for each candidate prefix and
    the root of the prefix tree. The prefix tree includes the prefixes and
    the siblings of each node visited.
    """
    if agg_id not in range(2):
        raise ValueError("invalid aggregator ID")
    if len(correction_words) != self.BITS:
        raise ValueError("corrections words has incorrect length")
    if level not in range(self.BITS):
        raise ValueError("level too deep")
    for prefix in prefixes:
        if len(prefix) != level + 1:
            raise ValueError("prefix with incorrect length")
    if len(set(prefixes)) != len(prefixes):
        raise ValueError("candidate prefixes are non-unique")

    # Evaluate our share of the prefix tree, including the sibling of each
    # node we visit.
    #
    # Implementation note: we can save computation by storing the tree
    # across `eval()` calls for the same report.
    root = PrefixTreeEntry.root(key, bool(agg_id))
    out_share = []
    for prefix in prefixes:
        n = root
        for (i, bit) in enumerate(prefix):
            idx = PrefixTreeIndex(prefix[:i+1])
            if n.left_child is None:
                n.left_child = self.eval_next(n, correction_words[i], ctx,
                                                nonce, idx.left_sibling())
            if n.right_child is None:
                n.right_child = self.eval_next(n, correction_words[i], ctx,
                                                nonce, idx.right_sibling())
            n = n.right_child if bit else n.left_child
        out_share.append(n.w if agg_id == 0 else vec_neg(n.w))

    return (out_share, root)
]]></sourcecode>
        <t>Obtaining our share of <tt>beta</tt>, the payload programmed into the prefix tree:</t>
        <sourcecode type="python"><![CDATA[
def get_beta_share(
        self,
        agg_id: int,
        correction_words: list[CorrectionWord],
        key: bytes,
        ctx: bytes,
        nonce: bytes,
) -> list[F]:
    root = PrefixTreeEntry.root(key, bool(agg_id))
    left = self.eval_next(root, correction_words[0], ctx, nonce,
                          PrefixTreeIndex((False,)))
    right = self.eval_next(root, correction_words[0], ctx, nonce,
                           PrefixTreeIndex((True,)))
    beta_share = vec_add(left.w, right.w)
    if agg_id == 1:
        beta_share = vec_neg(beta_share)
    return beta_share
]]></sourcecode>
      </section>
      <section anchor="vidpf-aux">
        <name>Auxiliary functions</name>
        <sourcecode type="python"><![CDATA[
def extend(self,
            seed: bytes,
            ctx: bytes,
            nonce: bytes,
            ) -> tuple[list[bytes], Ctrl]:
    '''
    Extend a seed into the seed and control bits for its left and
    right children in the VIDPF tree.
    '''
    xof = XofFixedKeyAes128(seed, dst(ctx, USAGE_EXTEND), nonce)
    s = [
        bytearray(xof.next(self.KEY_SIZE)),
        bytearray(xof.next(self.KEY_SIZE)),
    ]
    # Use the least significant bits as the control bit correction,
    # and then zero it out. This gives effectively 127 bits of
    # security, but reduces the number of AES calls needed by 1/3.
    t = [bool(s[0][0] & 1), bool(s[1][0] & 1)]
    s[0][0] &= 0xFE
    s[1][0] &= 0xFE
    return ([bytes(s[0]), bytes(s[1])], t)

def convert(self,
            seed: bytes,
            ctx: bytes,
            nonce: bytes,
            ) -> tuple[bytes, list[F]]:
    '''
    Convert a selected seed into a payload and the seed for the next
    level.
    '''
    xof = XofFixedKeyAes128(seed, dst(ctx, USAGE_CONVERT), nonce)
    next_seed = xof.next(XofFixedKeyAes128.SEED_SIZE)
    payload = xof.next_vec(self.field, self.VALUE_LEN)
    return (next_seed, payload)

def node_proof(self,
                seed: bytes,
                ctx: bytes,
                idx: PrefixTreeIndex) -> bytes:
    '''
    Compute the proof for this node.
    '''
    binder = \
        to_le_bytes(self.BITS, 2) + \
        to_le_bytes(idx.level(), 2) + \
        idx.encode()
    xof = XofTurboShake128(seed,
                            dst(ctx, USAGE_NODE_PROOF),
                            binder)
    return xof.next(PROOF_SIZE)
]]></sourcecode>
      </section>
    </section>
    <section anchor="vdaf">
      <name>Specification of Mastic</name>
      <t>An instance of Mastic is determined by a desired bit-length of the input,
denoted <tt>BITS</tt>, and a validity circuit, an instance of <tt>Valid</tt> as defined in
<xref section="7.3.2" sectionFormat="of" target="VDAF"/>. The validity circuit is used to instantiate the FLP
and defines the type of the weights generated by Clients and the type of the
total weight for each prefix computed by the Collector.</t>
      <t>The Client's measurement has two components: the input string <tt>alpha:
tuple[bool, ...]</tt> of length <tt>BITS</tt> and its weight. The weight's type is denoted
by <tt>W</tt>. We use <tt>beta: list[F]</tt> to denote the encoded weight, obtained by
invoking the FLP's encoder (<xref section="7.1.1" sectionFormat="of" target="VDAF"/>).</t>
      <t>The aggregate result has type <tt>list[R]</tt>, where <tt>R</tt> is likewise a type defined
by the validity circuit. Each element of this list corresponds to the total
weight for one of the candidate prefixes.</t>
      <t>The VIDPF is instantiated with bit length <tt>BITS</tt>, value length
<tt>valid.MEAS_LEN</tt>, and field <tt>valid.field</tt>, where <tt>valid</tt> is the validity
circuit. We denote this instance of the VIDPF by <tt>vidpf</tt>.</t>
      <t>In the remainder, we write <tt>xof</tt> as shorthand for <tt>XofTurboShake128</tt> (<xref section="6.2.1" sectionFormat="of" target="VDAF"/>).</t>
      <t>Mastic's implementation of the VDAF interface (<xref section="5" sectionFormat="of" target="VDAF"/>) is
specified in the following sections. <xref target="mastic-aux"/> defines some auxiliary
functions referenced in the sharding and preparation sections.</t>
      <section anchor="sharding">
        <name>Sharding</name>
        <t>The sharding algorithm takes in the measurement (the input and weight), the
nonce, and the sharding randomness. The size of the nonce is <tt>16</tt> bytes; the
size of the randomness is <tt>vidpf.RAND_SIZE + 2 * xof.SEED_SIZE</tt>, plus an
additional <tt>xof.SEED_SIZE</tt> if the validity circuit takes joint randomness as
input.</t>
        <t>The public share is the sequence of correction words output by the VIPDF key
generation algorithm. The contents of each input share, denoted
<tt>MasticInputShare</tt>, depends on the Aggregator who receives it. We refer to the
first Aggregator as the "Leader"; we refer to the second Aggregator as the
"Helper". The components of the input share are:</t>
        <ol spacing="normal" type="1"><li>
            <t>The Aggregator's VIDPF key share.</t>
          </li>
          <li>
            <t>An optional FLP proof share, a vector of field elements. This is set for the
Leader only.</t>
          </li>
          <li>
            <t>An optional seed. This is always set for the Helper, who uses it to derive
its FLP proof share. This is set for the Leader of the circuit uses joint
randomness.</t>
          </li>
          <li>
            <t>The peer's FLP joint randomness seed, used to derive joint randomness for
the FLP's validity circuit. This is set for both the Leader and Helper only
if joint randomness is required (i.e., <tt>flp.JOINT_RAND_LEN &gt; 0</tt>).</t>
          </li>
        </ol>
        <t>The behavior of the sharding algorithm depends on whether the circuit requires
joint randomness:</t>
        <sourcecode type="python"><![CDATA[
def shard(self,
            ctx: bytes,
            measurement: tuple[tuple[bool, ...], W],
            nonce: bytes,
            rand: bytes,
            ) -> tuple[list[CorrectionWord], list[MasticInputShare]]:
    if self.flp.JOINT_RAND_LEN > 0:
        return self.shard_with_joint_rand(
            ctx, measurement, nonce, rand)
    return self.shard_without_joint_rand(
        ctx, measurement, nonce, rand)
]]></sourcecode>
        <t>When no FLP joint randomness is required, sharding involves the following
steps:</t>
        <ol spacing="normal" type="1"><li>
            <t>Encode the weight <tt>weight</tt> as <tt>beta = [field(1)] + flp.encode(weight)</tt>. The
prefix <tt>[field(1)]</tt>, is used to count how many reports share a prefix in
common and is usesd during unsharding. More details in <xref target="unsharding"/>.</t>
          </li>
          <li>
            <t>Generate the VIDPF correction words and keys for input <tt>alpha</tt> and <tt>beta</tt> as
the payload.</t>
          </li>
          <li>
            <t>Generate the FLP proof of validity for <tt>flp.encode(weight)</tt>.</t>
          </li>
          <li>
            <t>Compute the Leader's share of the proof.</t>
          </li>
        </ol>
        <ul empty="true">
          <li>
            <t>NOTE Each correction word has length <tt>flp.MEAS_LEN</tt>. We could save a
little bit of communication by truncating the weights according to
<tt>flp.truncate()</tt>. However, we still need to encode the full weight at
least once, either separately or at the first level of the VIDPF tree.
See issue #98 for details.</t>
          </li>
        </ul>
        <t>The complete algorithm is listed below:</t>
        <sourcecode type="python"><![CDATA[
def shard_without_joint_rand(
        self,
        ctx: bytes,
        measurement: tuple[tuple[bool, ...], W],
        nonce: bytes,
        rand: bytes,
) -> tuple[list[CorrectionWord], list[MasticInputShare]]:
    (vidpf_rand, rand) = front(self.vidpf.RAND_SIZE, rand)
    (prove_rand_seed, rand) = front(self.xof.SEED_SIZE, rand)
    (helper_seed, rand) = front(self.xof.SEED_SIZE, rand)
    assert len(rand) == 0  # REMOVE ME

    # Encode the inputs to VIDPF key generation. The output, denoted
    # `beta`, is a counter concatenated with the encoded weight.
    (alpha, weight) = measurement
    beta = [self.field(1)] + self.flp.encode(weight)

    # Generate VIDPF keys.
    (correction_words, keys) = \
        self.vidpf.gen(alpha, beta, ctx, nonce, vidpf_rand)

    # Generate FLP and split it into shares.
    prove_rand = self.prove_rand(ctx, prove_rand_seed)
    proof = self.flp.prove(beta[1:], prove_rand, [])
    helper_proof_share = self.helper_proof_share(ctx, helper_seed)
    leader_proof_share = vec_sub(proof, helper_proof_share)

    input_shares: list[MasticInputShare] = [
        (keys[0], leader_proof_share, None, None),
        (keys[1], None, helper_seed, None),
    ]
    return (correction_words, input_shares)
]]></sourcecode>
        <t>When FLP joint randomness is required, the Client must compute it from the
shares of <tt>beta</tt> sent to each Aggregator:</t>
        <ol spacing="normal" type="1"><li>
            <t>Encode the weight <tt>weight</tt> as <tt>beta = [field(1)] + flp.encode(weight)</tt>.</t>
          </li>
          <li>
            <t>Generate the VIDPF correction words and keys for <tt>alpha</tt> and <tt>beta</tt>.</t>
          </li>
          <li>
            <t>Compute each Aggregator's FLP joint randomness part by hashing its share of
<tt>beta</tt> with its FLP seed, its VIDPF key, and the VIDPF correction words.</t>
          </li>
          <li>
            <t>Compute the FLP joint randomness seed by hashing the joint randomness parts.</t>
          </li>
          <li>
            <t>Derive the FLP joint randomness from the joint randomness seed.</t>
          </li>
          <li>
            <t>Generate the FLP proof of <tt>beta</tt>'s validity using the derived joint
randomness.</t>
          </li>
          <li>
            <t>Compute the Leader's share of the proof.</t>
          </li>
        </ol>
        <t>The joint randomness is also needed to verify the FLP and must therefore be
recomputed during preparation (<xref target="preparation"/>). To accomplish this, the Client
includes in each Aggregator's input share the joint randomness part of its peer.</t>
        <t>The complete algorithm is listed below:</t>
        <sourcecode type="python"><![CDATA[
def shard_with_joint_rand(
        self,
        ctx: bytes,
        measurement: tuple[tuple[bool, ...], W],
        nonce: bytes,
        rand: bytes,
) -> tuple[list[CorrectionWord], list[MasticInputShare]]:
    (vidpf_rand, rand) = front(self.vidpf.RAND_SIZE, rand)
    (prove_rand_seed, rand) = front(self.xof.SEED_SIZE, rand)
    (helper_seed, rand) = front(self.xof.SEED_SIZE, rand)
    (leader_seed, rand) = front(self.xof.SEED_SIZE, rand)
    assert len(rand) == 0  # REMOVE ME

    # Encode the inputs to VIDPF key generation. The output, denoted
    # `beta`, is a counter concatenated with the encoded weight.
    (alpha, weight) = measurement
    beta = [self.field(1)] + self.flp.encode(weight)

    # Generate VIDPF keys.
    (correction_words, keys) = \
        self.vidpf.gen(alpha, beta, ctx, nonce, vidpf_rand)

    # Generate FLP joint randomness.
    leader_beta_share = self.vidpf.get_beta_share(0, correction_words,
                                                    keys[0], ctx, nonce)
    helper_beta_share = self.vidpf.get_beta_share(1, correction_words,
                                                    keys[1], ctx, nonce)
    joint_rand_parts = [
        self.joint_rand_part(ctx, leader_seed, leader_beta_share[1:],
                                nonce),
        self.joint_rand_part(ctx, helper_seed, helper_beta_share[1:],
                                nonce),
    ]
    joint_rand = self.joint_rand(
        ctx, self.joint_rand_seed(ctx, joint_rand_parts))

    # Generate FLP and split it into shares.
    prove_rand = self.prove_rand(ctx, prove_rand_seed)
    proof = self.flp.prove(beta[1:], prove_rand, joint_rand)
    helper_proof_share = self.helper_proof_share(ctx, helper_seed)
    leader_proof_share = vec_sub(proof, helper_proof_share)

    leader_joint_rand_part: Optional[bytes] = joint_rand_parts[0]
    helper_joint_rand_part: Optional[bytes] = joint_rand_parts[1]
    input_shares = [
        (keys[0], leader_proof_share, leader_seed, helper_joint_rand_part),
        (keys[1], None, cast(Optional[bytes],
            helper_seed), leader_joint_rand_part),
    ]
    return (correction_words, input_shares)
]]></sourcecode>
      </section>
      <section anchor="preparation">
        <name>Preparation</name>
        <t>Each Aggregator initializes preparation with: the verification key shared by
both Aggregators; its own ID, either <tt>0</tt> for the Leader and <tt>1</tt> for the Helper;
the aggregation parameter; the report's nonce; the public share sent to each
Aggregator; and the Aggregator's own input share.</t>
        <t>The aggregation parameter has the following components:</t>
        <ol spacing="normal" type="1"><li>
            <t>the level of the VIDPF being evaluated</t>
          </li>
          <li>
            <t>the sequence of VIDPF prefixes being evaluated</t>
          </li>
          <li>
            <t>an indication of whether to verify the FLP</t>
          </li>
        </ol>
        <t>The FLP is verified exactly once, the first time the report is aggregated. See
<xref target="agg-param-validity"/>.</t>
        <t>The outputs of the initialization algorithm include the Aggregator's prep
state, denoted <tt>MasticPrepState</tt>, and its outbound prep share, denoted
<tt>MasticPrepShare</tt>. The prep share includes the Aggregator's FLP verifier share,
joint randomness part, and VIDPF proof. These are combined into the prep
message in the next step.</t>
        <t>Preparation initialization involves the following steps:</t>
        <ol spacing="normal" type="1"><li>
            <t>Evaluate the VIDPF share on the sequence of prefixes, obtaining our share of
the prefix tree.</t>
          </li>
          <li>
            <t>If applicable, run the FLP query generation algorithm on our share of the
encoded weight to obtain our FLP verifier share. If joint randomness is
required, then compute our joint randomness part and derive the joint
randomness seed using our peer Aggregator's part provided by the Client.
Note that the Client may have provided the wrong part, so we need to check
that the seed was computed correctly before completing preparation.</t>
          </li>
          <li>
            <t>Truncate each payload share according to the FLP encoding scheme and flatten
them into a single vector of field elements. This constitutes Mastic's
output share.</t>
          </li>
        </ol>
        <t>Moreover, when the Aggregators evaluate a Client's VIDPF, they verify three
properties of the prefix tree:</t>
        <ol spacing="normal" type="1"><li>
            <t>One-hotness: at every level of the tree, at most one node has a non-zero
payload.</t>
          </li>
          <li>
            <t>Payload consistency: each payload is equal to the sum of the payloads of its
children. If one-hotness holds, then this ensures the payload is equal to
<tt>beta</tt> for each node along the <tt>alpha</tt> path.</t>
          </li>
          <li>
            <t>Counter consistency: the counter of the non-zero payload is equal to
<tt>field(1)</tt>.</t>
          </li>
        </ol>
        <ul empty="true">
          <li>
            <t>TODO Define the "evaluation proof".</t>
          </li>
        </ul>
        <t>The complete algorithm is listed below:</t>
        <sourcecode type="python"><![CDATA[
def prep_init(
        self,
        verify_key: bytes,
        ctx: bytes,
        agg_id: int,
        agg_param: MasticAggParam,
        nonce: bytes,
        correction_words: list[CorrectionWord],
        input_share: MasticInputShare,
) -> tuple[MasticPrepState, MasticPrepShare]:
    (level, prefixes, do_weight_check) = agg_param
    (key, proof_share, seed, peer_joint_rand_part) = \
        self.expand_input_share(ctx, agg_id, input_share)

    # Evaluate the VIDPF.
    (out_share, root) = self.vidpf.eval_with_siblings(
        agg_id,
        correction_words,
        key,
        level,
        prefixes,
        ctx,
        nonce,
    )

    # Query the FLP if applicable.
    joint_rand_part = None
    joint_rand_seed = None
    verifier_share = None
    if do_weight_check:
        beta_share = self.vidpf.get_beta_share(agg_id, correction_words,
                                                key, ctx, nonce)
        query_rand = self.query_rand(verify_key, ctx, nonce, level)
        joint_rand = []
        if self.flp.JOINT_RAND_LEN > 0:
            assert seed is not None
            assert peer_joint_rand_part is not None
            joint_rand_part = self.joint_rand_part(ctx, seed,
                                                    beta_share[1:], nonce)
            if agg_id == 0:
                joint_rand_parts = [joint_rand_part, peer_joint_rand_part]
            else:
                joint_rand_parts = [peer_joint_rand_part, joint_rand_part]
            joint_rand_seed = self.joint_rand_seed(ctx, joint_rand_parts)
            joint_rand = self.joint_rand(ctx, joint_rand_seed)
        verifier_share = self.flp.query(
            beta_share[1:],
            proof_share,
            query_rand,
            joint_rand,
            2,
        )

    # Payload and onehot checks.
    payload_check_binder = b''
    onehot_check_binder = b''
    assert root.left_child is not None
    assert root.right_child is not None
    q = [root.left_child, root.right_child]
    while len(q) > 0:
        (n, q) = (q[0], q[1:])

        if n.left_child is not None and n.right_child is not None:
            # Update payload check. The weight of each node should equal
            # the sum of its children.
            payload_check_binder += self.field.encode_vec(
                vec_sub(n.w, vec_add(n.left_child.w, n.right_child.w)))
            q += [n.left_child, n.right_child]

        # Update the onehot check.
        onehot_check_binder += n.proof

    payload_check = self.xof(
        b'',
        dst_alg(ctx, USAGE_PAYLOAD_CHECK, self.ID),
        payload_check_binder,
    ).next(PROOF_SIZE)

    onehot_check = self.xof(
        b'',
        dst_alg(ctx, USAGE_ONEHOT_CHECK, self.ID),
        onehot_check_binder,
    ).next(PROOF_SIZE)

    # Counter check: the first element of beta should equal 1.
    #
    # Each aggregator holds an additive share of the counter, so
    # we have aggregator 1 negate its share and add 1 so that they
    # both compute the same value for `counter`.
    w0 = root.left_child.w
    w1 = root.right_child.w
    counter_check = self.field.encode_vec(
        [w0[0] + w1[0] + self.field(agg_id)])

    # Evaluation proof: if both aggregators compute the same
    # value, then they agree on the onehot proof, the counter, and
    # the payload.
    eval_proof = self.xof(
        verify_key,
        dst_alg(ctx, USAGE_EVAL_PROOF, self.ID),
        onehot_check + counter_check + payload_check,
    ).next(PROOF_SIZE)

    # Concatenate the output shares into one aggregatable output,
    # applying the FLP truncation algorithm on each FLP measurement
    # share.
    truncated_out_share = []
    for val_share in out_share:
        truncated_out_share += [val_share[0]] + \
            self.flp.truncate(val_share[1:])

    prep_state = (truncated_out_share, joint_rand_seed)
    prep_share = (eval_proof, verifier_share, joint_rand_part)
    return (prep_state, prep_share)
]]></sourcecode>
        <t>Next, the Aggregators' prep shares are combined into the prep message, denoted
<tt>MasticPrepMessage</tt>:</t>
        <ol spacing="normal" type="1"><li>
            <t>Check that both Aggregators computed the same VIDPF proof. If so, then it is
presumed that the output share is one-hot, has path consistency, and has
counter consistency as defined in <xref target="vidpf"/>.</t>
          </li>
          <li>
            <t>If applicable, combine the FLP verifier shares into the FLP verifier and run
the FLP decision algorithm. If successful, then it is presumed that the
weight is valid.</t>
          </li>
          <li>
            <t>If applicable, compute the FLP joint randomness seed from the parts.</t>
          </li>
        </ol>
        <t>The prep message consists of the optional joint randomness seed. The complete
algorithm is listed below:</t>
        <sourcecode type="python"><![CDATA[
def prep_shares_to_prep(
        self,
        ctx: bytes,
        agg_param: MasticAggParam,
        prep_shares: list[MasticPrepShare],
) -> MasticPrepMessage:
    (_level, _prefixes, do_weight_check) = agg_param

    if len(prep_shares) != 2:
        raise ValueError('unexpected number of prep shares')

    (eval_proof_0,
     verifier_share_0,
     joint_rand_part_0) = prep_shares[0]
    (eval_proof_1,
     verifier_share_1,
     joint_rand_part_1) = prep_shares[1]

    # Verify the VIDPF output.
    if eval_proof_0 != eval_proof_1:
        raise Exception('VIDPF verification failed')

    if not do_weight_check:
        return None
    if verifier_share_0 is None or verifier_share_1 is None:
        raise ValueError('expected FLP verifier shares')

    # Verify the FLP.
    verifier = vec_add(verifier_share_0, verifier_share_1)
    if not self.flp.decide(verifier):
        raise Exception('FLP verification failed')

    if self.flp.JOINT_RAND_LEN == 0:
        return None
    if joint_rand_part_0 is None or joint_rand_part_1 is None:
        raise ValueError('expected FLP joint randomness parts')

    # Confirm the FLP joint randomness was computed properly.
    prep_msg = self.joint_rand_seed(ctx, [
        joint_rand_part_0,
        joint_rand_part_1,
    ])
    return prep_msg
]]></sourcecode>
        <t>Finally, each Aggregator completes preparation by checking that the true FLP
joint randomness seed is equal to the value they computed in the initialization
step, <tt>prep_init()</tt>. This is only done if a weight check was required by the
aggregation parameter and joint randomness was required by the FLP:</t>
        <sourcecode type="python"><![CDATA[
def prep_next(self,
                _ctx: bytes,
                prep_state: MasticPrepState,
                prep_msg: MasticPrepMessage,
                ) -> list[F]:
    (truncated_out_share, joint_rand_seed) = prep_state
    if joint_rand_seed is not None:
        if prep_msg is None:
            raise ValueError('expected joint rand confirmation')

        if prep_msg != joint_rand_seed:
            raise Exception('joint rand confirmation failed')

    return truncated_out_share
]]></sourcecode>
      </section>
      <section anchor="agg-param-validity">
        <name>Validity of Aggregation Parameters</name>
        <t>To guarantee secure execution of Mastic, care must be taken in choosing the
VIDPF prefixes and whether to verify the FLP. In particular, it is only safe to
consume the FLP once; and it is only safe to evaluate the VIDPF at most once at
any given level of the tree.</t>
        <ul empty="true">
          <li>
            <t>NOTE By "safe" we mean "covered by the analysis of <xref target="MPDST25"/>". It could be
that we have a little more wiggle room, but we're not certain. If we find
matching attacks, we should mention them in <xref target="security-considerations"/>.</t>
          </li>
        </ul>
        <t>We further restrict aggregation by requiring that the level strictly increases
at each step:</t>
        <sourcecode type="python"><![CDATA[
def is_valid(self,
                agg_param: MasticAggParam,
                previous_agg_params: list[MasticAggParam],
                ) -> bool:
    (level, _prefixes, do_weight_check) = agg_param

    # Check that the weight check is done exactly once.
    weight_checked = \
        (do_weight_check and len(previous_agg_params) == 0) or \
        (not do_weight_check and
            any(agg_param[2] for agg_param in previous_agg_params))

    # Check that the level is strictly increasing.
    level_increased = len(previous_agg_params) == 0 or \
        level > previous_agg_params[-1][0]

    return weight_checked and level_increased
]]></sourcecode>
      </section>
      <section anchor="aggregation">
        <name>Aggregation</name>
        <t>Each output share consists of the truncated payload for each VIDPF prefix,
flattened into a single vector. Aggregation involves simply adding these up:</t>
        <sourcecode type="python"><![CDATA[
def agg_init(self, agg_param: MasticAggParam) -> list[F]:
    (_level, prefixes, _do_weight_check) = agg_param
    agg = self.field.zeros(len(prefixes)*(1+self.flp.OUTPUT_LEN))
    return agg

def agg_update(self,
                agg_param: MasticAggParam,
                agg_share: list[F],
                out_share: list[F]) -> list[F]:
    return vec_add(agg_share, out_share)

def merge(self,
            agg_param: MasticAggParam,
            agg_shares: list[list[F]]) -> list[F]:
    (_level, prefixes, _do_weight_check) = agg_param
    agg = self.agg_init(agg_param)
    for agg_share in agg_shares:
        agg = vec_add(agg, agg_share)
    return cast(list[F], agg)
]]></sourcecode>
      </section>
      <section anchor="unsharding">
        <name>Unsharding</name>
        <t>The aggregate result consists of a list of total weights, each corresponding to
one of the prefixes. To compute it:</t>
        <ol spacing="normal" type="1"><li>
            <t>Add up the aggregate shares.</t>
          </li>
          <li>
            <t>For each prefix, decode the corresponding vector chunk using the FLP's
decoding algorithm (<xref section="7.1.1" sectionFormat="of" target="VDAF"/>). This requires the prefix
count, which is also encoded by the chunk.</t>
          </li>
        </ol>
        <t>The complete algorithm is listed below:</t>
        <sourcecode type="python"><![CDATA[
def unshard(self,
            agg_param: MasticAggParam,
            agg_shares: list[list[F]],
            _num_measurements: int,
            ) -> list[R]:
    agg = self.merge(agg_param, agg_shares)

    agg_result = []
    while len(agg) > 0:
        (chunk, agg) = front(self.flp.OUTPUT_LEN + 1, agg)
        meas_count = chunk[0].int()
        agg_result.append(self.flp.decode(chunk[1:], meas_count))
    return agg_result
]]></sourcecode>
      </section>
      <section anchor="mastic-aux">
        <name>Auxiliary Functions</name>
        <sourcecode type="python"><![CDATA[
def expand_input_share(
        self,
        ctx: bytes,
        agg_id: int,
        input_share: MasticInputShare,
) -> tuple[bytes, list[F], Optional[bytes], Optional[bytes]]:
    if agg_id == 0:
        (key, proof_share, seed, peer_joint_rand_part) = input_share
        assert proof_share is not None
    else:
        (key, _leader_proof_share, seed, peer_joint_rand_part) = input_share
        assert seed is not None
        proof_share = self.helper_proof_share(ctx, seed)
    return (key, proof_share, seed, peer_joint_rand_part)

def helper_proof_share(self, ctx, seed: bytes) -> list[F]:
    return self.xof.expand_into_vec(
        self.field,
        seed,
        dst_alg(ctx, USAGE_PROOF_SHARE, self.ID),
        b'',
        self.flp.PROOF_LEN,
    )

def prove_rand(self, ctx: bytes, seed: bytes) -> list[F]:
    return self.xof.expand_into_vec(
        self.field,
        seed,
        dst_alg(ctx, USAGE_PROVE_RAND, self.ID),
        b'',
        self.flp.PROVE_RAND_LEN,
    )

def joint_rand_part(
        self,
        ctx: bytes,
        seed: bytes,
        weight_share: list[F],
        nonce: bytes,
) -> bytes:
    return self.xof.derive_seed(
        seed,
        dst_alg(ctx, USAGE_JOINT_RAND_PART, self.ID),
        nonce + self.field.encode_vec(weight_share),
    )

def joint_rand_seed(self, ctx: bytes, parts: list[bytes]) -> bytes:
    return self.xof.derive_seed(
        b'',
        dst_alg(ctx, USAGE_JOINT_RAND_SEED, self.ID),
        concat(parts),
    )

def joint_rand(self, ctx: bytes, seed: bytes) -> list[F]:
    return self.xof.expand_into_vec(
        self.field,
        seed,
        dst_alg(ctx, USAGE_JOINT_RAND, self.ID),
        b'',
        self.flp.JOINT_RAND_LEN,
    )

def query_rand(self,
               verify_key: bytes,
               ctx: bytes,
               nonce: bytes,
               level: int) -> list[F]:
    return self.xof.expand_into_vec(
        self.field,
        verify_key,
        dst_alg(ctx, USAGE_QUERY_RAND, self.ID),
        nonce + to_le_bytes(level, 2),
        self.flp.QUERY_RAND_LEN,
    )
]]></sourcecode>
      </section>
    </section>
    <section anchor="security-considerations">
      <name>Security Considerations</name>
      <t>Mastic inherits its security considerations from <xref section="9" sectionFormat="of" target="VDAF"/>. A
security analysis of Mastic is provided in <xref target="MPDST25"/>.</t>
      <ul empty="true">
        <li>
          <t>TODO Contrast with Poplar1, especially <xref section="9.4.2" sectionFormat="of" target="VDAF"/> ("Safe
Usage of IDPF Outputs"). In particular, it's perfectly safe to use Mastic's
intermediate outputs.</t>
        </li>
      </ul>
    </section>
    <section anchor="iana-considerations">
      <name>IANA Considerations</name>
      <t>IANA is requested to add new identifiers to the "Verifiable Distributed
Aggregation Functions (VDAF)" registry, that is described in <xref section="10" sectionFormat="of" target="VDAF"/>. The new entries to the VDAF Identifier registry are as follows:</t>
      <table anchor="codepoints">
        <name>Additional codepoints for the VDAF Identifier Registry.</name>
        <thead>
          <tr>
            <th align="left">Value</th>
            <th align="left">Scheme</th>
            <th align="left">Type</th>
            <th align="left">Reference</th>
          </tr>
        </thead>
        <tbody>
          <tr>
            <td align="left">
              <tt>0xFFFF0001</tt></td>
            <td align="left">MasticCount</td>
            <td align="left">VDAF</td>
            <td align="left">
              <xref target="vdaf"/> of RFC XXXX</td>
          </tr>
          <tr>
            <td align="left">
              <tt>0xFFFF0002</tt></td>
            <td align="left">MasticSum</td>
            <td align="left">VDAF</td>
            <td align="left">
              <xref target="vdaf"/> of RFC XXXX</td>
          </tr>
          <tr>
            <td align="left">
              <tt>0xFFFF0003</tt></td>
            <td align="left">MasticSumVec</td>
            <td align="left">VDAF</td>
            <td align="left">
              <xref target="vdaf"/> of RFC XXXX</td>
          </tr>
          <tr>
            <td align="left">
              <tt>0xFFFF0004</tt></td>
            <td align="left">MasticHistogram</td>
            <td align="left">VDAF</td>
            <td align="left">
              <xref target="vdaf"/> of RFC XXXX</td>
          </tr>
          <tr>
            <td align="left">
              <tt>0xFFFF0005</tt></td>
            <td align="left">MasticMultihotCountVec</td>
            <td align="left">VDAF</td>
            <td align="left">
              <xref target="vdaf"/> of RFC XXXX</td>
          </tr>
        </tbody>
      </table>
      <ul empty="true">
        <li>
          <t>TODO The codepoints in this section are from the private codepoint range and
are used for testing purposes. We will update this table with the codepoints
assigned by IANA.</t>
        </li>
      </ul>
      <t>(RFC EDITOR: Please replace "RFC XXXX" above with the RFC number assigned to
this document.)</t>
    </section>
  </middle>
  <back>
    <references anchor="sec-combined-references">
      <name>References</name>
      <references anchor="sec-normative-references">
        <name>Normative References</name>
        <reference anchor="VDAF">
          <front>
            <title>Verifiable Distributed Aggregation Functions</title>
            <author fullname="Richard Barnes" initials="R." surname="Barnes">
              <organization>Cisco</organization>
            </author>
            <author fullname="David Cook" initials="D." surname="Cook">
              <organization>ISRG</organization>
            </author>
            <author fullname="Christopher Patton" initials="C." surname="Patton">
              <organization>Cloudflare</organization>
            </author>
            <author fullname="Phillipp Schoppmann" initials="P." surname="Schoppmann">
              <organization>Google</organization>
            </author>
            <date day="10" month="January" year="2025"/>
            <abstract>
              <t>   This document describes Verifiable Distributed Aggregation Functions
   (VDAFs), a family of multi-party protocols for computing aggregate
   statistics over user measurements.  These protocols are designed to
   ensure that, as long as at least one aggregation server executes the
   protocol honestly, individual measurements are never seen by any
   server in the clear.  At the same time, VDAFs allow the servers to
   detect if a malicious or misconfigured client submitted an invalid
   measurement.  Two concrete VDAFs are specified, one for general-
   purpose aggregation (Prio3) and another for heavy hitters (Poplar1).

              </t>
            </abstract>
          </front>
          <seriesInfo name="Internet-Draft" value="draft-irtf-cfrg-vdaf-14"/>
        </reference>
        <reference anchor="RFC2119">
          <front>
            <title>Key words for use in RFCs to Indicate Requirement Levels</title>
            <author fullname="S. Bradner" initials="S." surname="Bradner"/>
            <date month="March" year="1997"/>
            <abstract>
              <t>In many standards track documents several words are used to signify the requirements in the specification. These words are often capitalized. This document defines these words as they should be interpreted in IETF documents. This document specifies an Internet Best Current Practices for the Internet Community, and requests discussion and suggestions for improvements.</t>
            </abstract>
          </front>
          <seriesInfo name="BCP" value="14"/>
          <seriesInfo name="RFC" value="2119"/>
          <seriesInfo name="DOI" value="10.17487/RFC2119"/>
        </reference>
        <reference anchor="RFC8174">
          <front>
            <title>Ambiguity of Uppercase vs Lowercase in RFC 2119 Key Words</title>
            <author fullname="B. Leiba" initials="B." surname="Leiba"/>
            <date month="May" year="2017"/>
            <abstract>
              <t>RFC 2119 specifies common key words that may be used in protocol specifications. This document aims to reduce the ambiguity by clarifying that only UPPERCASE usage of the key words have the defined special meanings.</t>
            </abstract>
          </front>
          <seriesInfo name="BCP" value="14"/>
          <seriesInfo name="RFC" value="8174"/>
          <seriesInfo name="DOI" value="10.17487/RFC8174"/>
        </reference>
      </references>
      <references anchor="sec-informative-references">
        <name>Informative References</name>
        <reference anchor="BGI15" target="https://www.iacr.org/archive/eurocrypt2015/90560300/90560300.pdf">
          <front>
            <title>Function Secret Sharing</title>
            <author initials="E." surname="Boyle">
              <organization/>
            </author>
            <author initials="N." surname="Gilboa">
              <organization/>
            </author>
            <author initials="Y." surname="Ishai">
              <organization/>
            </author>
            <date year="2015"/>
          </front>
          <seriesInfo name="EUROCRYPT 2015" value=""/>
        </reference>
        <reference anchor="CP22" target="https://iacr.org/cryptodb/data/paper.php?pubkey=31935">
          <front>
            <title>Lightweight, Maliciously Secure Verifiable Function Secret Sharing</title>
            <author initials="L." surname="de Castro">
              <organization/>
            </author>
            <author initials="A." surname="Polychroniadou">
              <organization/>
            </author>
            <date year="2022"/>
          </front>
          <seriesInfo name="EUROCRYPT 2022" value=""/>
        </reference>
        <reference anchor="MST24" target="https://ia.cr/2023/080">
          <front>
            <title>PLASMA: Private, Lightweight Aggregated Statistics against Malicious Adversaries</title>
            <author initials="D." surname="Mouris">
              <organization/>
            </author>
            <author initials="P." surname="Sarkar">
              <organization/>
            </author>
            <author initials="N. G." surname="Tsoutsos">
              <organization/>
            </author>
            <date year="2024"/>
          </front>
          <seriesInfo name="PETS 2024" value=""/>
        </reference>
        <reference anchor="MPDST25" target="https://ia.cr/2024/221">
          <front>
            <title>Mastic: Private Weighted Heavy-Hitters and Attribute-Based Metrics</title>
            <author initials="D." surname="Mouris">
              <organization/>
            </author>
            <author initials="C." surname="Patton">
              <organization/>
            </author>
            <author initials="H." surname="Davis">
              <organization/>
            </author>
            <author initials="P." surname="Sarkar">
              <organization/>
            </author>
            <author initials="N. G." surname="Tsoutsos">
              <organization/>
            </author>
            <date year="2025"/>
          </front>
          <seriesInfo name="PETS 2025" value=""/>
        </reference>
        <reference anchor="RZCGP24" target="https://eprint.iacr.org/2024/666">
          <front>
            <title>Private Analytics via Streaming, Sketching, and Silently Verifiable Proofs</title>
            <author initials="M." surname="Rathee">
              <organization/>
            </author>
            <author initials="Y." surname="Zhang">
              <organization/>
            </author>
            <author initials="H." surname="Corrigan-Gibbs">
              <organization/>
            </author>
            <author initials="R. A." surname="Popa">
              <organization/>
            </author>
            <date year="2024"/>
          </front>
          <seriesInfo name="IEEE S&amp;P 2024" value=""/>
        </reference>
        <reference anchor="W3C23" target="https://www.w3.org/TR/network-error-logging">
          <front>
            <title>Network Error Logging</title>
            <author initials="" surname="W3C Working Group">
              <organization/>
            </author>
            <date year="2023"/>
          </front>
        </reference>
        <reference anchor="RFC9110">
          <front>
            <title>HTTP Semantics</title>
            <author fullname="R. Fielding" initials="R." role="editor" surname="Fielding"/>
            <author fullname="M. Nottingham" initials="M." role="editor" surname="Nottingham"/>
            <author fullname="J. Reschke" initials="J." role="editor" surname="Reschke"/>
            <date month="June" year="2022"/>
            <abstract>
              <t>The Hypertext Transfer Protocol (HTTP) is a stateless application-level protocol for distributed, collaborative, hypertext information systems. This document describes the overall architecture of HTTP, establishes common terminology, and defines aspects of the protocol that are shared by all versions. In this definition are core protocol elements, extensibility mechanisms, and the "http" and "https" Uniform Resource Identifier (URI) schemes.</t>
              <t>This document updates RFC 3864 and obsoletes RFCs 2818, 7231, 7232, 7233, 7235, 7538, 7615, 7694, and portions of 7230.</t>
            </abstract>
          </front>
          <seriesInfo name="STD" value="97"/>
          <seriesInfo name="RFC" value="9110"/>
          <seriesInfo name="DOI" value="10.17487/RFC9110"/>
        </reference>
        <reference anchor="SHS">
          <front>
            <title>Secure hash standard</title>
            <author>
              <organization/>
            </author>
            <date year="2015"/>
          </front>
          <seriesInfo name="DOI" value="10.6028/nist.fips.180-4"/>
          <refcontent>National Institute of Standards and Technology (U.S.)</refcontent>
        </reference>
        <reference anchor="DAP">
          <front>
            <title>Distributed Aggregation Protocol for Privacy Preserving Measurement</title>
            <author fullname="Tim Geoghegan" initials="T." surname="Geoghegan">
              <organization>ISRG</organization>
            </author>
            <author fullname="Christopher Patton" initials="C." surname="Patton">
              <organization>Cloudflare</organization>
            </author>
            <author fullname="Eric Rescorla" initials="E." surname="Rescorla">
              <organization>Mozilla</organization>
            </author>
            <author fullname="Christopher A. Wood" initials="C. A." surname="Wood">
              <organization>Cloudflare</organization>
            </author>
            <date day="14" month="September" year="2023"/>
            <abstract>
              <t>   There are many situations in which it is desirable to take
   measurements of data which people consider sensitive.  In these
   cases, the entity taking the measurement is usually not interested in
   people's individual responses but rather in aggregated data.
   Conventional methods require collecting individual responses and then
   aggregating them, thus representing a threat to user privacy and
   rendering many such measurements difficult and impractical.  This
   document describes a multi-party distributed aggregation protocol
   (DAP) for privacy preserving measurement (PPM) which can be used to
   collect aggregate data without revealing any individual user's data.

              </t>
            </abstract>
          </front>
          <seriesInfo name="Internet-Draft" value="draft-ietf-ppm-dap-07"/>
        </reference>
      </references>
    </references>
    <?line 1393?>

<section numbered="false" anchor="acknowledgments">
      <name>Acknowledgments</name>
      <t>The Network Error Logging and Attribute-Based Browser Telemetry applications
(discussed next in section <xref target="motivation"/>) were first described to the authors by the
PPM working group at IETF. The authors would like to thank Suleman Ahmad and
Simon Friedberger for their help in fleshing out the details. Dimitris Mouris
and Nektarios Georgios Tsoutsos would like to acknowledge the support of the
National Science Foundation (Award 2239334).</t>
    </section>
    <section numbered="false" anchor="motivation">
      <name>Motivating Applications</name>
      <t>The design of Mastic is informed primarily by two use cases, which we describe
here.</t>
      <section numbered="false" anchor="NEL">
        <name>Network Error Logging</name>
        <t>Network Error Logging (NEL) is a mechanism used by web browsers to report
errors that occur while attempting to establish a connection to a server
<xref target="W3C23"/>. Some of these errors are visible to the server, but not all:
failures in DNS, TCP, TLS, and HTTP can occur without the server having any
visibility into the issue. A small amount of connection errors is expected,
even under normal operating conditions; but a sudden, substantial increase in
errors may be an indication of an outage, or a configuration issue impacting
millions of users. Without a reporting mechanism like NEL, these events would
only manifest in the server's telemetry as a drop in overall traffic.</t>
        <t>NEL is particularly important for content delivery networks that handle HTTP
traffic for a large number of websites (typically millions). A content delivery
network acts as a reverse proxy between clients and origin servers that
provides a layer of caching and security services, such as DDoS protection.</t>
        <t>Reports are comprised of the URL the client attempted to navigate to (e.g.,
"https://example.com"), the type of error that occurred, and metadata related
to the attempt, such as the time that elapsed between when the connection
attempt began and the error was observed (e.g., Section 7 of <xref target="W3C23"/>).
Clients may also report successful connection attempts to give the server a
sense of the error rate. The exact client behavior is determined by the
reporting policy specified by the server (see Section 5.1 of <xref target="W3C23"/>).</t>
        <t>NEL data is privacy-sensitive for two reasons. First, it exposes information
that the server would not otherwise have access to, which can be abused to
probe the client's network configuration as described in Section 9 of
<xref target="W3C23"/>. Second, for operational reasons, the reporting endpoint may be
organizationally separated from the server (i.e., run on different cloud
infrastructures), leading to an increased risk of the client's browsing
history being exposed (e.g., in a data breach).</t>
        <t>MPC helps mitigate these risks by revealing to the endpoint only the
information it needs to fulfill its service level objectives. This means, of
course, we must be satisfied with limited functionality. Fortunately, Mastic
allows us to preserve the most important functionality of NEL while minimizing
privacy loss.</t>
        <t>Mastic can be applied to a simplified version of NEL where each client reports
a tuple <tt>(dom, err)</tt> consisting of a domain name dom (e.g., "example.com") and
a value err that represents an error (e.g., "dns.unreachable") or an indication
that no error occurred (e.g., "ok"). Notably, this can be easily extended in
Mastic to represent more elaborate metrics. e.g., where each weight includes
the time it took each browser to report the error (and the aggregate is the
average error reporting time), user agent (browser type and version), etc.
However, our main goal is to understand 1) the distribution of errors and 2)
which domains are impacted.</t>
        <t>We expect there to be a large number of distinct domain names (millions in the
case of content delivery networks) and only a small number of error variants
(the NEL spec <xref target="W3C23"/> defines 30 variants). The following Mastic parameters
are suitable for this application.</t>
        <t>Each input would encode the domain <tt>dom</tt> encoded with a number of bits
sufficient to uniquely represent most of the domains; and each weight would
represent the error variant <tt>dom</tt>. To compute the distribution of errors, we
would encode each error variant as a distinct bucket of a histogram so that
<tt>[1, 0, 0, ...]</tt> represents "ok", <tt>[0, 1, 0, ...]</tt> represents
"dns.unreachable", and so on. (See ection 6 of <xref target="W3C23"/>.), This is similar to
Prio3Histogram (<xref section="7" sectionFormat="of" target="VDAF"/>.)</t>
      </section>
      <section numbered="false" anchor="attribute-based-telemetry">
        <name>Attribute-Based Browser Telemetry</name>
        <t>Web browsers collect telemetry generated by users as they navigate the web to
gain insights into trends that guide product decisions. In many cases, Prio3
(<xref section="7" sectionFormat="of" target="VDAF"/>) can be used to privately aggregate this telemetry.
However, this comes at the cost of flexibility.</t>
        <t>For example, Prio3 can be used to collect page load metrics from Browser for a
list of known popular sites (e.g., "example.com"). The purpose of these metrics
is to detect if changes to these sites cause regressions that might be
correlated with an increased average load time or error rate. A subtle, but
important requirement for this system is the ability to break down the metrics
by client attributes. Suppose for example that we want to aggregate by 1) the
software version, and 2) the information about the client's location.</t>
        <t>Mastic provides a simple solution to this problem. For the sake of
presentation, we consider a simplified use case (the same approach can be
applied to any aggregation task for which Prio3 (<xref section="7" sectionFormat="of" target="VDAF"/>) is
suitable). Each client reports a tuple <tt>(ver, loc, site, time)</tt> where: <tt>ver</tt>
is a string representing the client's software version (e.g.,
"Browser/122.0"); <tt>loc</tt> is a string encoding its country code (e.g., "GR",
"US", "IN", etc.); <tt>site</tt> is one of a fixed set of sites (e.g., "example.com",
"example.org", etc.); and <tt>time</tt> is the load time of the site in seconds. The
version and location are included in the Mastic input; the site and load time
are encoded by the corresponding weight. Notably, this is just one example of
what Mastic can do; the same idea can be applied to other types of metrics.</t>
        <t>Compared to the private NEL application in <xref target="NEL"/>, the number of possible
inputs here is relatively small: there are less than 200 country codes and a
handful of browser versions in wide use at any given time. This means the
aggregators can enumerate a set of inputs of interest and evaluate them
immediately. Consider the following parameters for Mastic, in its
attribute-based metrics mode of operation <xref target="attribute-based-metrics"/>:</t>
        <ul spacing="normal">
          <li>
            <t>Attributes: Two-letter country codes can easily be encoded in 2 bytes.
Likewise, the number of distinct browser versions is easily less than 2^16,
so 2 bytes are sufficient. Therefore, each attribute can be encoded with just
<tt>32</tt> bits.</t>
          </li>
          <li>
            <t>Values: Similar to private NEL, each weight is a <tt>0</tt>-vector except for a
single <tt>1</tt> representing a bucket in a histogram. We represent <tt>(site, time)</tt>
as a histogram bucket as follows. First, we quantize time (in seconds) into
one of four buckets: <tt>[0, 0.1)</tt>, <tt>[0.1, 1)</tt>, <tt>[1, 5)</tt>, and <tt>[5, inf)</tt>. Let <tt>0
&lt; t &lt;= 4</tt> denote the time bucket for <tt>time</tt>. Next, suppose we wish to track
metrics for <tt>25</tt> sites. Let <tt>0 &lt; s &lt;= 25</tt> denote the index of <tt>site</tt> in this
list. Then the index of 1 is simply <tt>t * s</tt>.</t>
          </li>
        </ul>
      </section>
    </section>
    <section numbered="false" anchor="additional-modes">
      <name>Modes of Operation</name>
      <section numbered="false" anchor="weighted-heavy-hitters">
        <name>Weighted Heavy-Hitters</name>
        <ul empty="true">
          <li>
            <t>NOTE See <xref target="NEL"/> for a motivating application and
<tt>example_weighted_heavy_hitters_mode()</tt> in the reference implementation for
an end-to-end example.</t>
          </li>
        </ul>
        <t>The primary use case for Mastic is a variant of the heavy-hitters problem, in
which the prefix counts are replaced with a notion of weight that is specific
to some application. For example, when measuring the performance of an ad
campaign, it is useful to learn not only which ads led to purchases, but how
much money was spent.</t>
        <t>To support this use case, we view the Client's <tt>alpha</tt> value as its measurement
and the <tt>beta</tt> value as the measurement's "weight". The range of valid values
for <tt>beta</tt> are therefore determined by the FLP with which Mastic is
instantiated. Concretely, validity of <tt>beta</tt> is expressed by a validity circuit
(<xref section="7.3.2" sectionFormat="of" target="VDAF"/>).</t>
        <t>To compute the weighted heavy-hitters, the Collector and Aggregators proceed as
described in <xref section="8" sectionFormat="of" target="VDAF"/>, except that the threshold represents a
minimum weight rather than a minimum count. In addition:</t>
        <ol spacing="normal" type="1"><li>
            <t>The Aggregators <bcp14>MUST</bcp14> perform the range check (i.e., verify the FLP) at the
first round of aggregation and remove any invalid reports before proceeding.</t>
          </li>
          <li>
            <t>The level at which the reports are Aggregated <bcp14>MUST</bcp14> be strictly increasing.</t>
          </li>
        </ol>
        <section numbered="false" anchor="different-thresholds">
          <name>Different Thresholds</name>
          <ul empty="true">
            <li>
              <t>NOTE For an end-to-end example, see
<tt>example_weighted_heavy_hitters_mode_with_different_thresholds()</tt> in the
reference implementation.</t>
            </li>
          </ul>
          <t>So far, we have assumed that there is a single threshold for determining which
prefixes are "heavy". However, we can easily extend this to have different
thresholds for different prefixes. There exist use-cases where prefixes
starting with "000" may be significantly more popular than prefixes starting
with "111". Setting a low threshold may result in an overwhelmingly big set of
heavy hitters starting with "000", while setting a high threshold might prune
anything starting with "111". Consider the following examples:</t>
          <ol spacing="normal" type="1"><li>
              <t>Popular URLs: <tt>a.example.com</tt> receives a massive amount of traffic whereas
<tt>b.example.com</tt> may have lower traffic. To identify heavy-hitting search
queries on <tt>a.example.com</tt>, the Aggregators should set a high threshold,
while queries with different domain prefixes may require lower thresholds to
be considered popular.</t>
            </li>
            <li>
              <t>E-commerce: Grocery items are essential and have a high volume of sales. In
contrast, electronics, though popular, usually come with a higher price
compared to groceries. Meanwhile, luxury items command significantly higher
prices but generally experience lower sales volumes. To identify
heavy-hitting grocery items on an e-commerce website, Aggregators could use
different threshold for each of these categories. These thresholds are set
to ensure that only the top-selling grocery items qualify as heavy hitters
while electronics and luxury items are also considered heavy hitters on
their own categories.</t>
            </li>
          </ol>
          <t>To tackle this, Mastic can allow different prefixes having different
thresholds. When a specific prefix does not have an associated threshold, we
first search if any of its prefixes has a specified threshold, otherwise we
use a default threshold. For example, if the Aggregators have set the
thresholds to be <tt>{"000": 10, "111": 2, "default": 5}</tt> and the search for
prefix "01", then threshold 5 should be used. However, if the Aggregators
search for prefix "11101", then threshold 2 should be used.</t>
        </section>
      </section>
      <section numbered="false" anchor="attribute-based-metrics">
        <name>Attribute-based Metrics</name>
        <ul empty="true">
          <li>
            <t>NOTE See <xref target="attribute-based-telemetry"/> for a motivating application and
<tt>example_attribute_based_metrics_mode()</tt> in the reference implementation for
an end-to-end example.</t>
          </li>
        </ul>
        <t>In this mode of operation, we take the <tt>beta</tt> value to be the Client's
measurement and <tt>alpha</tt> to be an arbitrary "attribute". For a given sequence of
attributes, the goal of the Collector is to aggregate the measurements that
share the same attribute. This provides functionality similar to Prio3
<xref target="VDAF"/>, except that the aggregate is partitioned by Clients who share some
property. For example, the attribute might encode the Client's user agent
<xref target="RFC9110"/>.</t>
        <t>Mastic requires each <tt>alpha</tt> to have the same length (<tt>Vidpf.BITS</tt>). Thus, it
is necessary for each application to choose a scheme for encoding attributes as
fixed-length strings. The following scheme is <bcp14>RECOMMENDED</bcp14>. Choose a
cryptographically secure hash function, such as SHA256
<xref target="SHS"/>, compute the hash of the Client's input
string, and interpret each bit of the hash as a bit of the VIDPF index.</t>
        <ul empty="true">
          <li>
            <t>TODO Are we comfortable recommending truncating the hash? Collisions aren't so
bad since the Client can just lie about <tt>alpha</tt> anyway. The main thing is to
pick a value for <tt>BITS</tt> that is large enough to avoid accidental collisions.</t>
          </li>
        </ul>
        <t>The Aggregators <bcp14>MAY</bcp14> aggregate a report any number times, but:</t>
        <ol spacing="normal" type="1"><li>
            <t>They <bcp14>MUST</bcp14> perform the range check (i.e., verify the FLP) the first time the
reports are aggregated and remove any invalid reports before aggregating
again.</t>
          </li>
          <li>
            <t>The aggregation parameter <bcp14>MUST</bcp14> specify the last level of the VIDPF tree
(i.e., <tt>level</tt> <bcp14>MUST</bcp14> be <tt>Vidpf.BITS-1</tt>).</t>
          </li>
        </ol>
        <ul empty="true">
          <li>
            <t>TODO Figure out if these requirements are strict enough. We may need to
tighten aggregation parameter validity if we find out that aggregating at the
same level more than once is not safe.</t>
          </li>
        </ul>
      </section>
      <section numbered="false" anchor="plain-heavy-hitters-with-proof-aggregation">
        <name>Plain Heavy-Hitters with VIDPF-Proof Aggregation</name>
        <ul empty="true">
          <li>
            <t>TODO Take "silently verifiable proofs" from <xref target="RZCGP24"/> into account
here, which allows us to aggregate the FLPs as well.</t>
          </li>
        </ul>
        <t>The total communication cost of using Mastic (or Poplar1 <xref target="VDAF"/>) for heavy
hitters is <tt>O(num_measurements * Vidpf.BITS)</tt> bits exchanged between the
Aggregators, where <tt>num_measurements</tt> is the number of reports being
aggregated. For plain heavy-hitters, this can be reduced to <tt>O(Vidpf.BITS)</tt> in
the best case.</t>
        <t>The idea is to take advantage of the feature of VIDPF evaluation whereby the
Aggregators compute identical VIDPF proofs if and only if the report is valid.
This allows the proofs themselves to be aggregated: if each report in a batch
of reports is valid, then the hash of their proofs will be equal as well; on
the other hand, if one report is invalid, then the hash of the proofs will not
be equal.</t>
        <t>To facilitate isolation of the invalid report(s), the proof strings are
arranged into a Merkle tree. During aggregation, the Aggregators interactively
traverse the tree to detect the subtree(s) containing invalid reports and
remove them from the batch.</t>
        <ul empty="true">
          <li>
            <t>TODO Decide if we should spell this out in greater detail. This feature
is not compatible with <xref target="DAP"/>; if we wanted to
extend DAP to support this, then we'd need to specify the wire format of the
messages exchanged between the Aggregators.</t>
          </li>
        </ul>
        <t>In the worst case, isolating invalid reports requires <tt>O(num_measurements *
Vidpf.BITS)</tt> bits of communication and <tt>Vidpf.BITS</tt> many rounds of
communication between the Aggregators. However, this behavior would only be
observed under attack conditions in which the vast majority of Clients are
malicious.</t>
        <t>In the simple case where the <tt>beta</tt> value is a constant (e.g., 1) we can
replace the FLP check with a simpler check. FLPs are not compatible with proof
aggregation the way VIDPFs are. In order to perform the range check without
FLPs, we use an extension of VIDPF described by <xref target="MST24"/>. The high-level idea
here is that the Aggregators can evaluate the empty string and verify that they
have shares of the constant <tt>beta</tt>. Next, as described in <xref target="vdaf"/>, we use the
"one-hot verifiability" and "path verifiability" checks to verify that each
level is non-zero at only a single point and that the same constant <tt>beta</tt> is
propagated down the tree correctly. Note that this trick is not suitable for
weighted heavy-hitters, since it expects that each <tt>beta</tt> value is constant
(e.g., 1).</t>
        <ul empty="true">
          <li>
            <t>TODO Proof aggregation could work with plain Mastic, but we would need
to check the FLPs at the first round of aggregation, leading to best-case
communication cost would be <tt>O(num_measurements + Vidpf.BITS)</tt>. This would be
OK, but we would still want to support a mode for plain heavy-hitters that is
as good as we can get.</t>
            <t>One idea is to always do the PLASMA <tt>0</tt>/<tt>1</tt> check alongside the FLP. This
would be useful for another reason: Usually FLP decoding requires
<tt>num_measurements</tt> as a parameter. We currently don't support this because we
currently don't have a pure counter as part of the VIDPF output.</t>
          </li>
        </ul>
      </section>
      <section numbered="false" anchor="malicious-security-with-three-aggregators">
        <name>Robustness Against a Malicious Aggregator</name>
        <t>Next, we describe an enhancement that allows Mastic to achieve robustness in
the presence of a malicious Aggregator. The two-party Mastic (as well as
Poplar1) is susceptible to additive attacks by a malicious Aggregator. In more
detail, if one of the Aggregators starts acting maliciously, they can
arbitrarily add to the aggregation result (simply by adding to their own
aggregation shares) without the honest Aggregator noticing.</t>
        <t>We can solve this problem in Mastic by using a technique from <xref target="MST24"/> that
lifts the two-party semi-honest secure PLASMA to the three-party maliciously
secure setting. Rather than having two Aggregators as in the previous setting,
this flavor involves three Aggregators, where every pair of Aggregators
communicate over a different channel. In essence, each pair of Aggregators
will run one session of the VDAF with unique randomness but on the same Client
measurement. The following changes are necessary:</t>
        <ol spacing="normal" type="1"><li>
            <t>The Client needs to generate three pairs of VIDPF keys all corresponding to
the same <tt>alpha</tt> and <tt>beta</tt> values. We represent the keys based on the
session as follows:
            </t>
            <ol spacing="normal" type="1"><li>
                <t>Session 0 (between Aggregators 0 and 1): <tt>key_01, key_10</tt></t>
              </li>
              <li>
                <t>Session 1 (between Aggregators 1 and 2): <tt>key_12, key_21</tt></t>
              </li>
              <li>
                <t>Session 2 (between Aggregators 2 and 0): <tt>key_20, key_02</tt></t>
              </li>
            </ol>
            <t>
Each pair of Aggregators cannot check that the Client input is consistent
across two sessions without the involvement of the third Aggregator. To
address this, we let two Aggregators (i.e., Aggregators 0 and 1) to run all
three sessions so that they can check that the Client input is consistent
across three sessions. The third Aggregator (i.e., Aggregator 2) is involved
as an attestator in two of the sessions. The check involves field addition
and subtraction and then hash comparisons.</t>
          </li>
          <li>
            <t>The Client sends the following keys to the Aggregators:
            </t>
            <ol spacing="normal" type="1"><li>
                <t>Aggregator 0 receives: <tt>key_01</tt>, <tt>key_02</tt>, and <tt>key_21</tt></t>
              </li>
              <li>
                <t>Aggregator 1 receives: <tt>key_10</tt>, <tt>key_12</tt>, and <tt>key_20</tt></t>
              </li>
              <li>
                <t>Aggregator 2 receives: <tt>key_21</tt> and <tt>key_20</tt></t>
              </li>
            </ol>
          </li>
          <li>
            <t>The Aggregators need to verify that the Client's input is consistent across
the different sessions (i.e., that all the keys correspond to the same
<tt>alpha</tt> and <tt>beta</tt> values). Aggregators 0 and 1 check that:
            </t>
            <ol spacing="normal" type="1"><li>
                <t>Their output shares of Session 0 minus their output shares of Session 1
 are shares of zero</t>
              </li>
              <li>
                <t>Their output shares of Session 1 minus their output shares of Session 2
are shares of zero.</t>
              </li>
            </ol>
            <t>
The subtraction is a local operation and verifying that two Aggregators
possess a sharing of zero requires exchanging one hash.</t>
          </li>
        </ol>
        <t>Using a third Aggregator, we can lift the security of Mastic from the
semi-honest setting to malicious security. While more complex to implement than
2-party Mastic, this mode allows achieves both privacy and robustness against a
malicious Aggregator.</t>
      </section>
    </section>
    <section numbered="false" anchor="test-vectors">
      <name>Test Vectors</name>
      <t>TODO</t>
    </section>
    <section anchor="contributors" numbered="false" toc="include" removeInRFC="false">
      <name>Contributors</name>
      <contact fullname="Pratik Sarkar">
        <organization>Supra Research</organization>
        <address>
          <email>pratik93@bu.edu</email>
        </address>
      </contact>
      <contact fullname="David Cook">
        <organization>ISRG</organization>
        <address>
          <email>dcook@divviup.org</email>
        </address>
      </contact>
    </section>
  </back>
  <!-- ##markdown-source:
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
n+b1p9NnT/9A+M4pKkqzbD43U25vO/gwW2DoJey9yU9c3P774/OzCTurNrgq
q4btP08R1ISds4zAx1a+jaUomLboXSj8nTHpu52EAsbG+aGOKtb/qukz115o
6NOjp69evDh6eXh0iEqWMko0K2/XbDdbX2msopYGwm2ydhddkN3Zjwd7X30N
OJ39ePbk8NXxaLw7+np375tHL4/PzkfPjk/ORuNvdodfYmt9dZP7M0hnIM+W
30imrrcuwQJMdEeLzeg99bYDps7eQ6nRwWYrl1F0UKbiPFot4HVDbADf8kpo
LEFU4SX36PhPfBDE5QrGlz+sUW74u3iKm2syHCQ3ceZh7BqgP9Ur5u4Vvr1J
bmUXWI4TiZgPF3W3zlC5xa8zzFhibTUSopHmLE7hOF4XpGMmsxmLKZxeYaap
xqZA8T34q3d8TLQt8zm1LcPMKKYXqzrffpa+zEJ8cKcXRAFfrXUXe91TdbbK
Nuk5MfLUURzJKtHdpdN46sKtZXJLZJJ1XIuG0k/oVZcz4U8mVuH2juxwPOk7
XHqGKELOAVNeyT5j68NVCU2KK8m2sbUaQrver4T6UKxA51vWYO04ma0DpWkL
Se0DxdgovjPUBotkpZGVRc4b1PxzpLUJNz5ZAglDcysL5AyW4QmXa/YTzz48
WKNJaIgdosmQM8qH3iK2cm5J6QG/2yFNO2VR/dolvXFH1Y7JLDz9z6c/nOwR
xdDSOjM29FA30ORM9GcQuxdyKUJIDr24ISlYT4XUG8GZ3+SGuJpgBinzoaS9
R2dQcw5jy5T6fDQZApGRdQmyk1e9ZtWL+Hexw5y+eEvA0TjkwAUfY9u8g2rC
1ybN7qyb1LmC3CHBufBvywOv471qW/BcsB3tzGYmShZNP5hrlnOo3BT+Nlgu
FHTswhRhgCWWZH5NupZmV/KxT5N6U3o3CXoXKfG6NAC5ozqyalvEaPzqyJUI
4xpCpiKpuyFQawCzfKFYIBZxbgrnIKk/16kRoR2IuCI6sxHTGfSHKQqsRR5k
zRCuHLrPrLLSjMRpY3CVcSFJxbdv40LAKC7eK74MNeNbs7wlKMXrHiHon05v
ZMYQJWqRzBAPIgJRsbRZGeKv8Slpr9KYeinCrhIBKFSUlKVgpBavepGWrJmh
IF58KNZ+72C3TRrMl5OZeJSRASEJCqyAonK8i7hhcWgzxVOaEFsM9P6/Jt2H
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

-->

</rfc>
