<?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.30 (Ruby 3.4.8) -->
<rfc xmlns:xi="http://www.w3.org/2001/XInclude" ipr="trust200902" docName="draft-ietf-privacypass-arc-crypto-00" category="info" submissionType="IETF" tocInclude="true" sortRefs="true" symRefs="true" version="3">
  <!-- xml2rfc v2v3 conversion 3.31.0 -->
  <front>
    <title abbrev="ARC Cryptography">Anonymous Rate-Limited Credentials Cryptography</title>
    <seriesInfo name="Internet-Draft" value="draft-ietf-privacypass-arc-crypto-00"/>
    <author initials="C." surname="Yun" fullname="Cathie Yun">
      <organization>Apple, Inc.</organization>
      <address>
        <email>cathieyun@gmail.com</email>
      </address>
    </author>
    <author initials="C. A." surname="Wood" fullname="Christopher A. Wood">
      <organization>Apple, Inc.</organization>
      <address>
        <email>caw@heapingbits.net</email>
      </address>
    </author>
    <author initials="A." surname="Faz-Hernandez" fullname="Armando Faz-Hernandez">
      <organization>Cloudflare</organization>
      <address>
        <email>armfazh@cloudflare.com</email>
      </address>
    </author>
    <date year="2026" month="February" day="03"/>
    <abstract>
      <?line 60?>

<t>This document specifies the Anonymous Rate-Limited Credential (ARC) protocol,
a specialization of keyed-verification anonymous credentials with support for
rate limiting. ARC credentials can be presented from client to server up to
some fixed number of times, where each presentation is cryptographically bound
to client secrets and application-specific public information, such that each
presentation is unlinkable from the others as well as the original credential
creation. ARC is useful in applications where a server needs to throttle or
rate-limit access from anonymous clients.</t>
    </abstract>
    <note removeInRFC="true">
      <name>About This Document</name>
      <t>
        The latest revision of this draft can be found at <eref target="https://ietf-wg-privacypass.github.io/draft-arc/draft-ietf-privacypass-arc-crypto.html"/>.
        Status information for this document may be found at <eref target="https://datatracker.ietf.org/doc/draft-ietf-privacypass-arc-crypto/"/>.
      </t>
      <t>
        Discussion of this document takes place on the
        PRIVACYPASS Privacy Pass mailing list (<eref target="mailto:privacy-pass@ietf.org"/>),
        which is archived at <eref target="https://mailarchive.ietf.org/arch/browse/privacy-pass"/>.
        Subscribe at <eref target="https://www.ietf.org/mailman/listinfo/privacy-pass/"/>.
      </t>
      <t>Source for this draft and an issue tracker can be found at
        <eref target="https://github.com/ietf-wg-privacypass/draft-arc"/>.</t>
    </note>
  </front>
  <middle>
    <?line 71?>

<section anchor="introduction">
      <name>Introduction</name>
      <t>This document specifies the Anonymous Rate-Limited Credential (ARC) protocol,
a specialization of keyed-verification anonymous credentials with support for
rate limiting.</t>
      <t>ARC is privately verifiable (keyed-verification), yet differs from similar token-based
protocols in that each credential can be presented multiple times without violating
unlinkability of different presentations. Servers issue credentials to clients that
are cryptographically bound to client secrets and some public information.
Afterwards, clients can present this credential to the server up to some fixed
number of times, where each presentation provides proof that it was derived
from a valid (previously issued) credential and bound to some public information.
Each presentation is pairwise unlinkable, meaning the server cannot link any two
presentations to the same client credential, nor can the server link a presentation
to the preceding credential issuance flow. Notably, the maximum number of
presentations from a credential is fixed by the application.</t>
      <t>ARC is useful in settings where applications require a fixed number of zero-knowledge
proofs about client secrets that can also be cryptographically bound to some public
information. This capability lets servers use credentials in applications that need
throttled or rate-limited access from anonymous clients.</t>
    </section>
    <section anchor="conventions-and-definitions">
      <name>Conventions and Definitions</name>
      <section anchor="notation-and-terminology">
        <name>Notation and Terminology</name>
        <t>The following functions and notation are used throughout the document.</t>
        <ul spacing="normal">
          <li>
            <t>concat(x0, ..., xN): Concatenation of byte strings. For example,
concat(0x01, 0x0203, 0x040506) = 0x010203040506.</t>
          </li>
          <li>
            <t>bytes_to_int and int_to_bytes: Convert a byte string to and from a non-negative integer.
bytes_to_int and int_to_bytes are implemented as OS2IP and I2OSP as described in
<xref target="RFC8017"/>, respectively. Note that these functions operate on byte strings
in big-endian byte order.</t>
          </li>
          <li>
            <t>random_integer_uniform(M, N): Generate a random, uniformly distributed integer R
between M inclusive and N exclusive, i.e., M &lt;= R &lt; N.</t>
          </li>
          <li>
            <t>random_integer_uniform_excluding_set(M, N, S): Generate a random, uniformly
distributed integer R between M inclusive and N exclusive, i.e., M &lt;= R &lt; N,
such that R does not exist in the set of integers S.</t>
          </li>
        </ul>
        <t>All algorithms and procedures described in this document are laid out
in a Python-like pseudocode. Each function takes a set of inputs and parameters
and produces a set of output values. Parameters become constant values once the
protocol variant and the ciphersuite are fixed.</t>
        <t>The notation <tt>T U[N]</tt> refers to an array called U containing N items of type
T. The type <tt>opaque</tt> means one single byte of uninterpreted data. Items of
the array are zero-indexed and referred as <tt>U[j]</tt> such that 0 &lt;= j &lt; N.
The notation <tt>{T}</tt> refers to a set consisting of elements of type <tt>T</tt>.
For any object <tt>x</tt>, we write <tt>len(x)</tt> to denote its length in bytes.</t>
        <t>String values such as "CredentialRequest", "CredentialResponse", "Presentation", and "Tag"
are ASCII string literals.</t>
        <t>The following terms are used throughout this document.</t>
        <ul spacing="normal">
          <li>
            <t>Client: Protocol initiator. Creates a credential request, and uses the
corresponding server response to make a credential. The client can
make multiple presentations of this credential.</t>
          </li>
          <li>
            <t>Server: Computes a response to a credential request, with its
server private keys. Later the server can verify the client's presentations
with its private keys. Learns nothing about the client's secret attributes,
and cannot link a client's request/response and presentation steps.</t>
          </li>
        </ul>
        <!-- TODO(caw): define these terms:
- tag
- attribute
- requestContext
- presentationContext
- presentationLimit
- presentation
-->

</section>
    </section>
    <section anchor="preliminaries">
      <name>Preliminaries</name>
      <t>The construction in this document has one primary dependency:</t>
      <ul spacing="normal">
        <li>
          <t><tt>Group</tt>: A prime-order group implementing the API described below in <xref target="pog"/>.
See <xref target="ciphersuites"/> for specific instances of groups.</t>
        </li>
      </ul>
      <section anchor="pog">
        <name>Prime-Order Group</name>
        <t>In this document, we assume the construction of an additive, prime-order
group <tt>Group</tt> for performing all mathematical operations. In prime-order groups,
any element (other than the identity) can generate the other elements of the
group. Usually, one element is fixed and defined as the group generator.
In the ARC setting, there are two fixed generator elements (generatorG, generatorH).
Such groups are uniquely determined by the choice of the prime <tt>p</tt> that defines the
order of the group. (There may, however, exist different representations
of the group for a single <tt>p</tt>. <xref target="ciphersuites"/> lists specific groups which
indicate both order and representation.)</t>
        <t>The fundamental group operation is addition <tt>+</tt> with identity element
<tt>I</tt>. For any elements <tt>A</tt> and <tt>B</tt> of the group, <tt>A + B = B + A</tt> is
also a member of the group. Also, for any <tt>A</tt> in the group, there exists an element
<tt>-A</tt> such that <tt>A + (-A) = (-A) + A = I</tt>. Scalar multiplication by <tt>r</tt> is
equivalent to the repeated application of the group operation on an
element A with itself <tt>r-1</tt> times, this is denoted as <tt>r*A = A + ... + A</tt>.
For any element <tt>A</tt>, <tt>p*A=I</tt>. The case when the scalar multiplication is
performed on the group generator is denoted as <tt>ScalarMultGen(r)</tt>.
Given two elements A and B, the discrete logarithm problem is to find
an integer k such that B = k*A. Thus, k is the discrete logarithm of
B with respect to the base A.
The set of scalars corresponds to <tt>GF(p)</tt>, a prime field of order p, and are
represented as the set of integers defined by <tt>{0, 1, ..., p-1}</tt>.
This document uses types
<tt>Element</tt> and <tt>Scalar</tt> to denote elements of the group and its set of
scalars, respectively.</t>
        <t>We now detail a number of member functions that can be invoked on a
prime-order group.</t>
        <ul spacing="normal">
          <li>
            <t>Order(): Outputs the order of the group (i.e. <tt>p</tt>).</t>
          </li>
          <li>
            <t>Identity(): Outputs the identity element of the group (i.e. <tt>I</tt>).</t>
          </li>
          <li>
            <t>Generator(): Outputs the fixed generator of the group.</t>
          </li>
          <li>
            <t>HashToGroup(x, info): Deterministically maps
an array of bytes <tt>x</tt> with domain separation value <tt>info</tt> to an element of <tt>Group</tt>. The map must ensure that,
for any adversary receiving <tt>R = HashToGroup(x, info)</tt>, it is
computationally difficult to reverse the mapping.
Security properties of this function are described
in <xref target="I-D.irtf-cfrg-hash-to-curve"/>.</t>
          </li>
          <li>
            <t>HashToScalar(x, info): Deterministically maps
an array of bytes <tt>x</tt> with domain separation value <tt>info</tt> to an element in GF(p).
Security properties of this function are described in <xref section="10.5" sectionFormat="comma" target="I-D.irtf-cfrg-hash-to-curve"/>.</t>
          </li>
          <li>
            <t>RandomScalar(): Chooses at random a non-zero element in GF(p).</t>
          </li>
          <li>
            <t>ScalarInverse(s): Returns the inverse of input <tt>Scalar</tt> <tt>s</tt> on <tt>GF(p)</tt>.</t>
          </li>
          <li>
            <t>SerializeElement(A): Maps an <tt>Element</tt> <tt>A</tt>
to a canonical byte array <tt>buf</tt> of fixed length <tt>Ne</tt>.</t>
          </li>
          <li>
            <t>DeserializeElement(buf): Attempts to map a byte array <tt>buf</tt> to
an <tt>Element</tt> <tt>A</tt>, and fails if the input is not the valid canonical byte
representation of an element of the group. This function can raise a
DeserializeError if deserialization fails or <tt>A</tt> is the identity element of
the group; see <xref target="ciphersuites"/> for group-specific input validation steps.</t>
          </li>
          <li>
            <t>SerializeScalar(s): Maps a <tt>Scalar</tt> <tt>s</tt> to a canonical
byte array <tt>buf</tt> of fixed length <tt>Ns</tt>.</t>
          </li>
          <li>
            <t>DeserializeScalar(buf): Attempts to map a byte array <tt>buf</tt> to a <tt>Scalar</tt> <tt>s</tt>.
This function can raise a DeserializeError if deserialization fails; see
<xref target="ciphersuites"/> for group-specific input validation steps.</t>
          </li>
        </ul>
        <t>For each group, there exists two distinct generators, generatorG and
generatorH, generatorG = G.Generator() and generatorH = G.HashToGroup(G.SerializeElement(generatorG), "generatorH").
The group member functions GeneratorG() and GeneratorH() are shorthand
for returning generatorG and generatorH, respectively.</t>
        <t><xref target="ciphersuites"/> contains details for the implementation of this interface
for different prime-order groups instantiated over elliptic curves.</t>
      </section>
    </section>
    <section anchor="arc-protocol">
      <name>ARC Protocol</name>
      <t>The ARC protocol is a two-party protocol run between client and server
consisting of three distinct phases:</t>
      <ol spacing="normal" type="1"><li>
          <t>Key generation. In this phase, the server generates its private and public
keys to be used for the remaining phases. This phase is described in <xref target="setup"/>.</t>
        </li>
        <li>
          <t>Credential issuance. In this phase, the client and server interact to issue
the client a credential that is cryptographically bound to client secrets.
This phase is described in <xref target="issuance"/>.</t>
        </li>
        <li>
          <t>Presentation. In this phase, the client uses the credential to create a "presentation"
to the server, where the server learns nothing more than whether or not the
presentation is valid and corresponds to some previously issued credential,
without learning which credential it corresponds to. This phase is described
in <xref target="presentation"/>.</t>
        </li>
      </ol>
      <t>This protocol bears resemblance to anonymous token protocols, such as those built on
Blind RSA <xref target="BLIND-RSA"/> and Oblivious Pseudorandom Functions <xref target="OPRFS"/>
with one critical distinction: unlike anonymous tokens, an anonymous credential can be
used multiple times to create unlinkable presentations (up to the fixed presentation
limit). This means that a single issuance invocation can drive multiple presentation
invocations, whereas with anonymous tokens, each presentation invocation requires
exactly one issuance invocation. As a result, credentials are generally longer lived
than tokens. Applications configure the credential presentation limit after the
credential is issued such that client and server agree on the limit during presentation.
Servers are responsible for ensuring this limit is not exceeded. Clients that exceed
the agreed-upon presentation limit break the unlinkability guarantees provided by
the protocol.</t>
      <t>The rest of this section describes the three phases of the ARC protocol.</t>
      <section anchor="setup">
        <name>Key Generation</name>
        <t>In the key generation phase, the server generates its private and public
keys, denoted ServerPrivateKey and ServerPublicKey, as follows.</t>
        <artwork><![CDATA[
Input: None
Output:
- ServerPrivateKey:
  - x0: Scalar
  - x1: Scalar
  - x2: Scalar
  - x0Blinding: Scalar
- ServerPublicKey:
  - X0: Element
  - X1: Element
  - X2: Element

Parameters
- Group G

def SetupServer():
  x0 = G.RandomScalar()
  x1 = G.RandomScalar()
  x2 = G.RandomScalar()
  x0Blinding = G.RandomScalar()
  X0 = x0 * G.GeneratorG() + x0Blinding * G.GeneratorH()
  X1 = x1 * G.GeneratorH()
  X2 = x2 * G.GeneratorH()
  return ServerPrivateKey(x0, x1, x2, x0Blinding), ServerPublicKey(X0, X1, X2)
]]></artwork>
        <t>The server public keys can be serialized as follows:</t>
        <artwork><![CDATA[
struct {
  uint8 X0[Ne]; // G.SerializeElement(X0)
  uint8 X1[Ne]; // G.SerializeElement(X1)
  uint8 X2[Ne]; // G.SerializeElement(X2)
} ServerPublicKey;
]]></artwork>
        <t>The length of this encoded response structure is <tt>NserverPublicKey = 3*Ne</tt>.</t>
      </section>
      <section anchor="issuance">
        <name>Issuance</name>
        <t>The purpose of the issuance phase is for the client and server to cooperatively compute a credential
that is cryptographically bound to the client's secrets. Clients do not choose these secrets;
they are computed by the protocol.</t>
        <t>The issuance phase of the protocol requires clients to know the server public key a priori, as well as
an arbitrary, application-specific request context. It requires no other input. It consists of three
distinct steps:</t>
        <ol spacing="normal" type="1"><li>
            <t>The client generates and sends a credential request to the server. This credential request contains a
proof that the request is valid with respect to the client's secrets and request context. See
<xref target="issuance-step1"/> for details about this step.</t>
          </li>
          <li>
            <t>The server validates the credential request. If valid, it computes a credential response with the server
private keys. The response includes a proof that the credential response is valid with respect to the
server keys. The server sends the response to the client. See <xref target="issuance-step2"/> for details about this
step.</t>
          </li>
          <li>
            <t>The client finalizes the credential by processing the server response. If valid, this step yields a
credential that can then be used in the presentation phase of the protocol. See <xref target="issuance-step3"/> for
details about this step.</t>
          </li>
        </ol>
        <t>Each of these steps are described in the following subsections.</t>
        <section anchor="issuance-step1">
          <name>Credential Request</name>
          <t>Given a request context, the process for creating a credential request is as follows:</t>
          <artwork><![CDATA[
(clientSecrets, request) = CreateCredentialRequest(requestContext)

Inputs:
- requestContext: Data, context for the credential request

Outputs:
- request:
  - m1Enc: Element, first encrypted secret.
  - m2Enc: Element, second encrypted secret.
  - requestProof: ZKProof, a proof of correct generation of m1Enc and m2Enc.
- clientSecrets:
  - m1: Scalar, first secret.
  - m2: Scalar, second secret.
  - r1: Scalar, blinding factor for first secret.
  - r2: Scalar, blinding factor for second secret.

Parameters:
- G: Group
- generatorG: Element, equivalent to G.GeneratorG()
- generatorH: Element, equivalent to G.GeneratorH()

def CreateCredentialRequest(requestContext):
  m1 = G.RandomScalar()
  m2 = G.HashToScalar(requestContext, "requestContext")
  r1 = G.RandomScalar()
  r2 = G.RandomScalar()
  m1Enc = m1 * generatorG + r1 * generatorH
  m2Enc = m2 * generatorG + r2 * generatorH
  requestProof = MakeCredentialRequestProof(m1, m2, r1, r2, m1Enc, m2Enc)
  request = (m1Enc, m2Enc, requestProof)
  clientSecrets = (m1, m2, r1, r2)
  return (clientSecrets, request)
]]></artwork>
          <t>See <xref target="request-proof"/> for more details on the generation of the credential request proof.</t>
          <t>The resulting request can be serialized as follows.</t>
          <artwork><![CDATA[
struct {
  uint8 m1Enc[Ne];
  uint8 m2Enc[Ne];
  uint8 challenge[Ns];
  uint8 response0[Ns];
  uint8 response1[Ns];
  uint8 response2[Ns];
  uint8 response3[Ns];
} CredentialRequest;
]]></artwork>
          <t>The length of this encoded request structure is <tt>Nrequest = 2*Ne + 5*Ns</tt>.</t>
        </section>
        <section anchor="issuance-step2">
          <name>Credential Response</name>
          <t>Given a credential request and server public and private keys, the process
for creating a credential response is as follows.</t>
          <sourcecode type="pseudocode"><![CDATA[
response = CreateCredentialResponse(serverPrivateKey, serverPublicKey, request)

Inputs:
- serverPrivateKey:
  - x0: Scalar (private), server private key 0.
  - x1: Scalar (private), server private key 1.
  - x2: Scalar (private), server private key 2.
  - x0Blinding: Scalar (private), blinding value for x0.
- serverPublicKey:
  - X0: Element, server public key 0.
  - X1: Element, server public key 1.
  - X2: Element, server public key 2.
- request:
  - m1Enc: Element, first encrypted secret.
  - m2Enc: Element, second encrypted secret.
  - requestProof: ZKProof, a proof of correct generation of m1Enc and m2Enc.

Outputs:
- U: Element, a randomized generator for the response, `b*G`.
- encUPrime: Element, encrypted UPrime.
- X0Aux: Element, auxiliary point for X0.
- X1Aux: Element, auxiliary point for X1.
- X2Aux: Element, auxiliary point for X2.
- HAux: Element, auxiliary point for generatorH.
- responseProof: ZKProof, a proof of correct generation of
  U, encUPrime, server public keys, and auxiliary points.

Parameters:
- G: Group
- generatorG: Element, equivalent to G.GeneratorG()
- generatorH: Element, equivalent to G.GeneratorH()

Exceptions:
- VerifyError, raised when response verification fails

def CreateCredentialResponse(serverPrivateKeys, serverPublicKey, request):
  if VerifyCredentialRequestProof(request) == false:
    raise VerifyError

  b = G.RandomScalar()
  U = b * generatorG
  encUPrime = b * (serverPublicKey.X0 +
        serverPrivateKeys.x1 * request.m1Enc +
        serverPrivateKeys.x2 * request.m2Enc)
  X0Aux = b * serverPrivateKeys.x0Blinding * generatorH
  X1Aux = b * serverPublicKey.X1
  X2Aux = b * serverPublicKey.X2
  HAux = b * generatorH

  responseProof = MakeCredentialResponseProof(serverPrivateKey,
    serverPublicKey, request, b, U, encUPrime, X0Aux, X1Aux, X2Aux, HAux)
  return (U, encUPrime, X0Aux, X1Aux, X2Aux, HAux, responseProof)
]]></sourcecode>
          <t>The resulting response can be serialized as follows. See <xref target="response-proof"/> for more details on the generation of the credential response proof.</t>
          <artwork><![CDATA[
struct {
  uint8 U[Ne];
  uint8 encUPrime[Ne];
  uint8 X0Aux[Ne];
  uint8 X1Aux[Ne];
  uint8 X2Aux[Ne];
  uint8 HAux[Ne];
  uint8 challenge[Ns];
  uint8 response0[Ns];
  uint8 response1[Ns];
  uint8 response2[Ns];
  uint8 response3[Ns];
  uint8 response4[Ns];
  uint8 response5[Ns];
  uint8 response6[Ns];
} CredentialResponse
]]></artwork>
          <t>The length of this encoded response structure is <tt>Nresponse = 6*Ne + 8*Ns</tt>.</t>
        </section>
        <section anchor="issuance-step3">
          <name>Finalize Credential</name>
          <t>Given a credential request and response, server public keys, and the client
secrets produced when creating a credential request, the process for
finalizing the issuance flow and creating a credential is as follows.</t>
          <artwork><![CDATA[
credential = FinalizeCredential(clientSecrets, serverPublicKey, request, response)

Inputs:
- clientSecrets:
  - m1: Scalar, first secret.
  - m2: Scalar, second secret.
  - r1: Scalar, blinding factor for first secret.
  - r2: Scalar, blinding factor for second secret.
- serverPublicKey: ServerPublicKey, shared with the client out-of-band
- request:
  - m1Enc: Element, first encrypted secret.
  - m2Enc: Element, second encrypted secret.
  - requestProof: ZKProof, a proof of correct generation of m1Enc and m2Enc.
- response:
  - U: Element, a randomized generator for the response. `b*G`.
  - encUPrime: Element, encrypted UPrime.
  - X0Aux: Element, auxiliary point for X0.
  - X1Aux: Element, auxiliary point for X1.
  - X2Aux: Element, auxiliary point for X2.
  - HAux: Element, auxiliary point for generatorH.
  - responseProof: ZKProof, a proof of correct generation of U, encUPrime, server public keys, and auxiliary points.

Outputs:
- credential:
  - m1: Scalar, client's first secret.
  - U: Element, a randomized generator for the response. `b*G`.
  - UPrime: Element, the MAC over the server's private keys and the client's secret secrets.
  - X1: Element, server public key 1.

Exceptions:
- VerifyError, raised when response verification fails

Parameters:
- G: Group
- generatorG: Element, equivalent to G.GeneratorG()
- generatorH: Element, equivalent to G.GeneratorH()

def FinalizeCredential(clientSecrets, serverPublicKey, request, response):
  if VerifyCredentialResponseProof(serverPublicKey, response, request) == false:
    raise VerifyError
  UPrime = response.encUPrime - response.X0Aux - clientSecrets.r1 * response.X1Aux - clientSecrets.r2 * response.X2Aux
  return (clientSecrets.m1, response.U, UPrime, serverPublicKey.X1)
]]></artwork>
        </section>
      </section>
      <section anchor="presentation">
        <name>Presentation</name>
        <t>The purpose of the presentation phase is for the client to create a "presentation" to the server
which can be verified using the server private key. This phase is non-interactive, i.e., there is
no state stored between client and server in order to produce and then verify a presentation.
Client and server agree upon a fixed limit of presentations in order to create and verify
presentations; presentations will not verify correctly if the client and server use different
limits.</t>
        <t>This phase consists of three steps:</t>
        <ol spacing="normal" type="1"><li>
            <t>The client creates a presentation state for a given presentation context and presentation limit.
This state is used to produce a fixed amount of presentations.</t>
          </li>
          <li>
            <t>The client creates a presentation from the presentation state and sends it to the server.
The presentation is cryptographically bound to the state's presentation context, and
contains proof that the presentation is valid with respect to the presentation context.
Moreover, the presentation contains proof that the nonce (an integer) associated with this
presentation is within the presentation limit.</t>
          </li>
          <li>
            <t>The server verifies the presentation with respect to the presentation context and presentation
limit.</t>
          </li>
        </ol>
        <t>Details for each each of these steps are in the following subsections.</t>
        <section anchor="presentation-state">
          <name>Presentation State</name>
          <t>Presentation state is used to track the number of presentations for a given credential.
This state is important for ARC's unlinkability goals: reuse of state can break
unlinkability properties of credential presentations. State is initialized
with a credential, presentation context, and presentation limit. It is then mutated
after each presentation construction (as described in <xref target="presentation-construction"/>).</t>
          <artwork><![CDATA[
state = MakePresentationState(credential, presentationContext, presentationLimit)

Inputs:
- credential:
  - m1: Scalar, client's first secret.
  - U: Element, a randomized generator for the response `b*G`.
  - UPrime: Element, the MAC over the server's private keys and the client's secrets.
  - X1: Element, server public key 1.
- presentationContext: Data (public), used for presentation tag computation.
- presentationLimit: Integer, the fixed presentation limit.

Outputs:
- credential
- presentationContext: Data (public), used for presentation tag computation.
- presentationNonceSet: {Integer}, the set of nonces that have been used for this presentation
- presentationLimit: Integer, the fixed presentation limit.

def MakePresentationState(credential, presentationContext, presentationLimit):
  return PresentationState(credential, presentationContext, [], presentationLimit)
]]></artwork>
        </section>
        <section anchor="presentation-construction">
          <name>Presentation Construction</name>
          <t>Creating a presentation requires a credential, presentation context, and presentation limit.
This process is necessarily stateful on the client since the number of times a credential
is used for a given presentation context cannot exceed the presentation limit; doing so
would break presentation unlinkability, as two presentations created with the same nonce
can be directly compared for equality (via the "tag"). As a result, the process for creating
a presentation accepts as input a presentation state and then outputs an updated presentation
state.</t>
          <artwork><![CDATA[
newState, nonce, presentation = Present(state)

Inputs:
state: input PresentationState
  - credential
  - presentationContext: Data (public), used for presentation tag computation.
  - presentationNonceSet: {Integer}, the set of nonces that have been used for this presentation
  - presentationLimit: Integer, the fixed presentation limit.

Outputs:
- newState: updated PresentationState
- nonce: Integer, the nonce associated with this presentation.
- presentation:
  - U: Element, re-randomized from the U in the response.
  - UPrimeCommit: Element, a public key to the issued UPrime.
  - m1Commit: Element, a public key to the client secret (m1).
  - tag: Element, the tag element used for enforcing the presentation limit.
  - presentationProof: ZKProof, a proof of correct generation of the presentation.

Parameters:
- G: Group
- generatorG: Element, equivalent to G.GeneratorG()
- generatorH: Element, equivalent to G.GeneratorH()

Exceptions:
- LimitExceededError, raised when the presentation count meets or exceeds the presentation limit for the given presentation context

def Present(state):
  if len(state.presentationNonceSet) >= state.presentationLimit:
    raise LimitExceededError

  a = G.RandomScalar()
  r = G.RandomScalar()
  z = G.RandomScalar()

  U = a * state.credential.U
  UPrime = a * state.credential.UPrime
  UPrimeCommit = UPrime + r * generatorG
  m1Commit = state.credential.m1 * U + z * generatorH

  # This step mutates the state by keeping track of
  # what nonces have already been spent.
  nonce = random_integer_uniform_excluding_set(0,
    state.presentationLimit, state.presentationNonceSet)
  state.presentationNonceSet.add(nonce)

  generatorT = G.HashToGroup(presentationContext, "Tag")
  tag = (credential.m1 + nonce)^(-1) * generatorT
  V = z * credential.X1 - r * generatorG
  m1Tag = state.credential.m1 * tag

  presentationProof = MakePresentationProof(U, UPrimeCommit, m1Commit, tag, generatorT, credential, V, r, z, nonce, m1Tag)

  presentation = (U, UPrimeCommit, m1Commit, tag, presentationProof)

  return state, nonce, presentation
]]></artwork>
          <t>OPEN ISSUE: should the tag also fold in the presentation limit?</t>
          <t>The resulting presentation can be serialized as follows. See <xref target="presentation-proof"/>
for more details on the generation of the presentation proof.</t>
          <artwork><![CDATA[
struct {
  uint8 U[Ne];
  uint8 UPrimeCommit[Ne];
  uint8 m1Commit[Ne];
  uint8 tag[Ne];
  uint8 challenge[Ns];
  uint8 response0[Ns];
  uint8 response1[Ns];
  uint8 response2[Ns];
  uint8 response3[Ns];
} Presentation
]]></artwork>
          <t>The length of this structure is <tt>Npresentation = 4*Ne + 5*Ns</tt>.</t>
        </section>
        <section anchor="presentation-verification">
          <name>Presentation Verification</name>
          <t>The server processes the presentation by verifying the presentation proof against server-computed
values, and performing a check that the presentation conforms to the presentation limit.</t>
          <artwork><![CDATA[
validity, tag = VerifyPresentation(
  serverPrivateKey,
  serverPublicKey,
  requestContext,
  presentationContext,
  nonce,
  presentation,
  presentationLimit)

Inputs:
- serverPrivateKey:
  - x0: Scalar (private), server private key 0.
  - x1: Scalar (private), server private key 1.
  - x2: Scalar (private), server private key 2.
  - x0Blinding: Scalar (private), blinding value for x0.
- serverPublicKey:
  - X0: Element, server public key 0.
  - X1: Element, server public key 1.
  - X2: Element, server public key 2.
- requestContext: Data, context for the credential request.
- presentationContext: Data (public), used for presentation tag computation.
- nonce: Integer, the nonce associated with this presentation.
- presentation:
  - U: Element, re-randomized from the U in the response.
  - UPrimeCommit: Element, a public key to the issued UPrime.
  - m1Commit: Element, a public key to the client secret (m1).
  - tag: Element, the tag element used for enforcing the presentation limit.
  - presentationProof: ZKProof, a proof of correct generation of the presentation.
- presentationLimit: Integer, the fixed presentation limit.

Outputs:
- validity: Boolean, True if the presentation is valid, False otherwise.
- tag: Bytes, the value of the presentation tag used for rate limiting.

Parameters:
- G: Group
- generatorG: Element, equivalent to G.GeneratorG()
- generatorH: Element, equivalent to G.GeneratorH()

Exceptions:
- InvalidNonceError, raised when the nonce associated with the presentation is invalid

def VerifyPresentation(
  serverPrivateKey,
  serverPublicKey,
  requestContext,
  presentationContext,
  nonce,
  presentation,
  presentationLimit):

  if nonce < 0 or nonce > presentationLimit:
    raise InvalidNonceError

  generatorT = G.HashToGroup(presentationContext, "Tag")
  m1Tag = generatorT - (nonce * presentation.tag)

  validity = VerifyPresentationProof(
    serverPrivateKey,
    serverPublicKey,
    requestContext,
    presentationContext,
    presentation,
    m1Tag)

  return validity, presentation.tag
]]></artwork>
          <t>Implementation-specific steps: the server must perform a check that the tag (presentation.tag) has
not previously been seen, to prevent double spending. It then stores the tag for use in future double
spending checks. To reduce the overhead of performing double spend checks, the server can store and
look up the tags corresponding to the associated requestContext and presentationContext values.</t>
        </section>
      </section>
    </section>
    <section anchor="zero-knowledge-proofs">
      <name>Zero-Knowledge Proofs</name>
      <t>This section describes a Schnorr proof compiler that is used for the construction of other proofs needed throughout
the ARC protocol. <xref target="compiler"/> describes the compiler, and the remaining sections describe how it is used for
the purposes of producing ARC proofs.</t>
      <section anchor="compiler">
        <name>Schnorr Compiler</name>
        <t>The compiler specified in this section automates the Fiat-Shamir transform that is often used to
transform interactive zero-knowledge proofs into non-interactive proofs such that they can be used
to non-interactively prove various statements of importance in higher-level protocols, such as ARC.
The compiler consists of a prover and verifier role. The prover constructs a transcript for the
proof and then applies the Fiat-Shamir heuristic to generate the resulting challenge and response
values. The verifier reconstructs the same transcript to verify the proof.</t>
        <t>The prover and verifier roles are specified below in <xref target="prover"/> and <xref target="verifier"/>, respectively.</t>
        <section anchor="prover">
          <name>Prover</name>
          <t>The prover role consists of four functions:</t>
          <ul spacing="normal">
            <li>
              <t>AppendScalar: This function adds a scalar representation to the transcript.</t>
            </li>
            <li>
              <t>AppendElement: This function adds an element representation to the transcript.</t>
            </li>
            <li>
              <t>Constrain: This function applies an explicit constraint to the proof, where the constraint is expressed as equality between some element and a linear combination of scalar and element representations. An example constraint might be <tt>Z = aX + bY</tt>, for scalars <tt>a</tt>, <tt>b</tt>, and elements
<tt>X</tt>, <tt>Y</tt>, <tt>Z</tt>.</t>
            </li>
            <li>
              <t>Prove: This function applies the Fiat-Shamir heuristic to the protocol transcript and set of
constraints to produce a zero-knowledge proof that can be verified.</t>
            </li>
          </ul>
          <t>These functions are defined in the following sub-sections.</t>
          <t>In addition, the prover role consists of the following state:</t>
          <ul spacing="normal">
            <li>
              <t>label: Data, a value representing the context in which the proof will be used</t>
            </li>
            <li>
              <t>scalars: [Integer], An ordered set of representation of scalar variables to use in the proof. Each scalar has a label associated with it, stored in a list called <tt>scalar_labels</tt>.</t>
            </li>
            <li>
              <t>elements: [Integer], An ordered set of representation of element variables to use in the proof. Each element has a label associated with it, stored in a list called <tt>element_labels</tt>.</t>
            </li>
            <li>
              <t>constraints: a set of constraints, where each constraint consists of a constraint element and a linear combination of variables.</t>
            </li>
          </ul>
          <section anchor="appendscalar">
            <name>AppendScalar</name>
            <artwork><![CDATA[
AppendScalar(label, assignment)

Inputs:
- label: Data, Scalar variable label
- assignment: Scalar variable

Outputs:
- Integer representation of the new scalar variable

def AppendScalar(label, assignment):
  state.scalars.append(assignment)
  state.scalar_labels.append(label)
  return len(state.scalars) - 1
]]></artwork>
          </section>
          <section anchor="appendelement">
            <name>AppendElement</name>
            <artwork><![CDATA[
AppendElement(label, assignment)

Inputs:
- label: Data, Element variable label
- assignment: Element variable

Outputs:
- Integer representation of the new element variable

def AppendElement(label, assignment):
  state.elements.append(assignment)
  state.element_labels.append(label)
  return len(state.elements) - 1
]]></artwork>
          </section>
          <section anchor="constrain">
            <name>Constrain</name>
            <artwork><![CDATA[
Constrain(result, linearCombination)

Inputs:
- result: Integer, representation of constraint element
- assignment: linear combination of scalar and element variable (representations)

def Constrain(label, linearCombination):
  state.constraints.append((result, linearCombination))
]]></artwork>
          </section>
          <section anchor="prove">
            <name>Prove</name>
            <t>The Prove function is defined below.</t>
            <artwork><![CDATA[
Prove()

Outputs:
- ZKProof, a proof consisting of a challenge Scalar and then fixed number of response Scalar values

Parameters:
- G: Group
- generatorG: Element, equivalent to G.GeneratorG()
- generatorH: Element, equivalent to G.GeneratorH()

Exceptions:
- InvalidVariableAllocationError, raised when the prover was incorrectly configured

def Prove():
  blindings = [G.RandomScalar() for i in range(len(state.scalars))]

  blinded_elements = []
  for (constraint_point, linear_combination) in state.constraints:
    if constraint_point.index > len(state.elements):
      raise InvalidVariableAllocationError

    for (scalar_var, element_var) in linear_combination:
      if scalar_var.index > len(state.scalars):
        raise InvalidVariableAllocationError
      if element_var.index > len(state.elements):
        raise InvalidVariableAllocationError

    scalar_index = linear_combination[0][0]
    element_index = linear_combination[0][1]
    blinded_element = blindings[scalar_index] * state.elements[element_index]

    for i, pair in enumerate(linear_combination):
      if i > 0:
        scalar_index = pair[0]
        element_index = pair[1]
        blinded_element += blindings[scalar_index] * state.elements[element_index]

        blinded_elements.append(blinded_element)

  # Obtain a scalar challenge
  challenge = ComposeChallenge(state.label, state.elements, blinded_elements)

  # Compute response scalars from the challenge, scalars, and blindings.
  responses = []
  for (index, scalar) in enumerate(state.scalars):
    blinding = blindings[index]
    responses.append(blinding - challenge * scalar)

  return ZKProof(challenge, responses)
]]></artwork>
            <t>The function ComposeChallenge is defined below.</t>
            <artwork><![CDATA[
ComposeChallenge(label, elements, blinded_elements)

Inputs:
- label: Data, the proof label
- elements: [Element], ordered list of elements
- blinded_elements: [Element], ordered list of blinded elements

Outputs:
- challenge, Scalar

Parameters:
- G: Group
- generatorG: Element, equivalent to G.GeneratorG()
- generatorH: Element, equivalent to G.GeneratorH()

def ComposeChallenge(label, elements, blinded_elements):
  challenge_input = Data() # Empty Data

  for element in elements:
    serialized_element = G.SerializeElement(element)
    challenge_input += I2OSP(len(serialized_element), 2) + serialized_element

  for blinded_element in blinded_elements:
    serialized_blinded_element = G.SerializeElement(blinded_element)
    challenge_input += I2OSP(len(serialized_blinded_element), 2) + serialized_blinded_element

  return G.HashToScalar(challenge_input, label)
]]></artwork>
          </section>
        </section>
        <section anchor="verifier">
          <name>Verifier</name>
          <t>The verifier role consists of four functions:</t>
          <ul spacing="normal">
            <li>
              <t>AppendScalar: This function adds a scalar representation to the transcript.</t>
            </li>
            <li>
              <t>AppendElement: This function adds an element representation to the transcript.</t>
            </li>
            <li>
              <t>Constrain: This function applies an explicit constraint to the proof, where the constraint is expressed as equality between some element and a linear combination of scalar and element representations. An example constraint might be <tt>Z = aX + bY</tt>, for scalars <tt>a</tt>, <tt>b</tt>, and elements
<tt>X</tt>, <tt>Y</tt>, <tt>Z</tt>.</t>
            </li>
            <li>
              <t>Verify: This function applies the Fiat-Shamir heuristic to verify the zero-knowledge proof.</t>
            </li>
          </ul>
          <t>AppendScalar and Verify are defined in the following sub-sections. AppendElement and Constrain matches the functionality used in the prover role.</t>
          <section anchor="appendscalar-1">
            <name>AppendScalar</name>
            <artwork><![CDATA[
AppendScalar(label)

Inputs:
- label: Data, Scalar variable label

Outputs:
- Integer representation of the new scalar variable

def AppendScalar(label):
  state.scalar_labels.append(label)
  return len(state.scalar_labels) - 1
]]></artwork>
          </section>
          <section anchor="verify">
            <name>Verify</name>
            <artwork><![CDATA[
Verify(proof)

Inputs:
- ZKProof, a proof consisting of a challenge Scalar and then fixed number of response Scalar values

Outputs:
- Boolean, True if the proof is valid, False otherwise.

Parameters:
- G: Group
- generatorG: Element, equivalent to G.GeneratorG()
- generatorH: Element, equivalent to G.GeneratorH()

Exceptions:
- InvalidVariableAllocationError, raised when the prover was incorrectly configured

def Verify(proof):
  if len(state.elements) != len(state.element_labels):
    raise InvalidVariableAllocationError

  blinded_elements = []
  for (constraint_element, linear_combination) in state.constraints:
    if constraint_element > len(state.elements):
      raise InvalidVariableAllocationError
    for (_, element_var) in linear_combination:
      if element_var > len(state.elements):
        raise InvalidVariableAllocationError

    challenge_element = proof.challenge * state.elements[constraint_element]
    for i, pair in enumerate(linear_combination):
      challenge_element += proof.responses[pair[0]] * state.elements[pair[1]]

    blinded_elements.append(challenge_element)

  challenge = ComposeChallenge(state.label, self.elements, blinded_elements)
  return challenge == proof.challenge
]]></artwork>
          </section>
        </section>
      </section>
      <section anchor="request-proof">
        <name>CredentialRequest Proof</name>
        <t>The request proof is a proof of knowledge of (m1, m2, r1, r2) used to generate the encrypted request. Statements to prove:</t>
        <artwork><![CDATA[
1. m1Enc = m1 * generatorG + r1 * generatorH
2. m2Enc = m2 * generatorG + r2 * generatorH
]]></artwork>
        <section anchor="credentialrequest-proof-creation">
          <name>CredentialRequest Proof Creation</name>
          <artwork><![CDATA[
requestProof = MakeCredentialRequestProof(m1, m2, r1, r2, m1Enc, m2Enc)

Inputs:
- m1: Scalar, first secret.
- m2: Scalar, second secret.
- r1: Scalar, blinding factor for first secret.
- r2: Scalar, blinding factor for second secret.
- m1Enc: Element, first encrypted secret.
- m2Enc: Element, second encrypted secret.

Outputs:
- proof: ZKProof
  - challenge: Scalar, the challenge used in the proof of valid encryption.
  - response0: Scalar, the response corresponding to m1.
  - response1: Scalar, the response corresponding to m2.
  - response2: Scalar, the response corresponding to r1.
  - response3: Scalar, the response corresponding to r2.

Parameters:
- G: Group
- generatorG: Element, equivalent to G.GeneratorG()
- generatorH: Element, equivalent to G.GeneratorH()
- contextString: public input

def MakeCredentialRequestProof(m1, m2, r1, r2, m1Enc, m2Enc):
  prover = Prover(contextString + "CredentialRequest")

  m1Var = prover.AppendScalar("m1", m1)
  m2Var = prover.AppendScalar("m2", m2)
  r1Var = prover.AppendScalar("r1", r1)
  r2Var = prover.AppendScalar("r2", r2)

  genGVar = prover.AppendElement("genG", generatorG)
  genHVar = prover.AppendElement("genH", generatorH)
  m1EncVar = prover.AppendElement("m1Enc", m1Enc)
  m2EncVar = prover.AppendElement("m2Enc", m2Enc)

  # 1. m1Enc = m1 * generatorG + r1 * generatorH
  prover.Constrain(m1EncVar, [(m1Var, genGVar), (r1Var, genHVar)])

  # 2. m2Enc = m2 * generatorG + r2 * generatorH
  prover.Constrain(m2EncVar, [(m2Var, genGVar), (r2Var, genHVar)])

  return prover.Prove()
]]></artwork>
        </section>
        <section anchor="credentialrequest-proof-verification">
          <name>CredentialRequest Proof Verification</name>
          <artwork><![CDATA[
validity = VerifyCredentialRequestProof(request)

Inputs:
- request:
  - m1Enc: Element, first encrypted secret.
  - m2Enc: Element, second encrypted secret.
  - requestProof: ZKProof, a proof of correct generation of m1Enc and m2Enc.
    - challenge: Scalar, the challenge used in the proof of valid encryption.
    - response0: Scalar, the response corresponding to m1.
    - response1: Scalar, the response corresponding to m2.
    - response2: Scalar, the response corresponding to r1.
    - response3: Scalar, the response corresponding to r2.

Outputs:
- validity: Boolean, True if the proof verifies correctly, False otherwise.

Parameters:
- G: group
- generatorG: Element, equivalent to G.GeneratorG()
- generatorH: Element, equivalent to G.GeneratorH()
- contextString: public input

def VerifyCredentialRequestProof(request):
  verifier = Verifier(contextString + "CredentialRequest")

  m1Var = verifier.AppendScalar("m1")
  m2Var = verifier.AppendScalar("m2")
  r1Var = verifier.AppendScalar("r1")
  r2Var = verifier.AppendScalar("r2")

  genGVar = verifier.AppendElement("genG", generatorG)
  genHVar = verifier.AppendElement("genH", generatorH)
  m1EncVar = verifier.AppendElement("m1Enc", request.m1Enc)
  m2EncVar = verifier.AppendElement("m2Enc", request.m2Enc)

  # 1. m1Enc = m1 * generatorG + r1 * generatorH
  verifier.Constrain(m1EncVar, [(m1Var, genGVar), (r1Var, genHVar)])

  # 2. m2Enc = m2 * generatorG + r2 * generatorH
  verifier.Constrain(m2EncVar, [(m2Var, genGVar), (r2Var, genHVar)])

  return verifier.Verify(request.proof)
]]></artwork>
        </section>
      </section>
      <section anchor="response-proof">
        <name>CredentialResponse Proof</name>
        <t>The response proof is a proof of knowledge of (x0, x1, x2, x0Blinding, b) used in the server's CredentialResponse for the client's CredentialRequest. Statements to prove:</t>
        <artwork><![CDATA[
1. X0 = x0 * generatorG + x0Blinding * generatorH
2. X1 = x1 * generatorH
3. X2 = x2 * generatorH
4. X0Aux = b * x0Blinding * generatorH
  4a. HAux = b * generatorH
  4b: X0Aux = x0Blinding * HAux (= b * x0Blinding * generatorH)
5. X1Aux = b * x1 * generatorH
  5a. X1Aux = t1 * generatorH (t1 = b * x1)
  5b. X1Aux = b * X1 (X1 = x1 * generatorH)
6. X2Aux = b * x2 * generatorH
  6a. X2Aux = b * X2 (X2 = x2 * generatorH)
  6b. X2Aux = t2 * generatorH (t2 = b * x2)
7. U = b * generatorG
8. encUPrime = b * (X0 + x1 * Enc(m1) + x2 * Enc(m2))
]]></artwork>
        <section anchor="credentialresponse-proof-creation">
          <name>CredentialResponse Proof Creation</name>
          <artwork><![CDATA[
responseProof = MakeCredentialResponseProof(serverPrivateKey, serverPublicKey, request, b, U, encUPrime, X0Aux, X1Aux, X2Aux, HAux)

Inputs:
- serverPrivateKey:
  - x0: Scalar (private), server private key 0.
  - x1: Scalar (private), server private key 1.
  - x2: Scalar (private), server private key 2.
  - x0Blinding: Scalar (private), blinding value for x0.
- serverPublicKey:
  - X0: Element, server public key 0.
  - X1: Element, server public key 1.
  - X2: Element, server public key 2.
- request:
  - m1Enc: Element, first encrypted secret.
  - m2Enc: Element, second encrypted secret.
  - requestProof: ZKProof, a proof of correct generation of m1Enc and m2Enc.
- encUPrime: Element, encrypted UPrime.
- X0Aux: Element, auxiliary point for X0.
- X1Aux: Element, auxiliary point for X1.
- X2Aux: Element, auxiliary point for X2.
- HAux: Element, auxiliary point for generatorH.

Outputs:
- proof: ZKProof
  - challenge: Scalar, the challenge used in the proof of valid response.
  - response0: Scalar, the response corresponding to x0.
  - response1: Scalar, the response corresponding to x1.
  - response2: Scalar, the response corresponding to x2.
  - response3: Scalar, the response corresponding to x0Blinding.
  - response4: Scalar, the response corresponding to b.
  - response5: Scalar, the response corresponding to t1.
  - response6: Scalar, the response corresponding to t2.

Parameters:
- G: Group
- generatorG: Element, equivalent to G.GeneratorG()
- generatorH: Element, equivalent to G.GeneratorH()
- contextString: public input

def MakeCredentialResponseProof(serverPrivateKey, serverPublicKey, request, b, U, encUPrime, X0Aux, X1Aux, X2Aux, HAux):
  prover = Prover(contextString + "CredentialResponse")

  x0Var = prover.AppendScalar("x0", serverPrivateKey.x0)
  x1Var = prover.AppendScalar("x1", serverPrivateKey.x1)
  x2Var = prover.AppendScalar("x2", serverPrivateKey.x2)
  x0BlindingVar = prover.AppendScalar("x0Blinding", serverPrivateKey.x0Blinding)
  bVar = prover.AppendScalar("b", b)
  t1Var = prover.AppendScalar("t1", b * serverPrivateKey.x1)
  t2Var = prover.AppendScalar("t2", b * serverPrivateKey.x2)

  genGVar = prover.AppendElement("genG", generatorG)
  genHVar = prover.AppendElement("genH", generatorH)
  m1EncVar = prover.AppendElement("m1Enc", request.m1Enc)
  m2EncVar = prover.AppendElement("m2Enc", request.m2Enc)
  UVar = prover.AppendElement("U", U)
  encUPrimeVar = prover.AppendElement("encUPrime", encUPrime)
  X0Var = prover.AppendElement("X0", serverPublicKey.X0)
  X1Var = prover.AppendElement("X1", serverPublicKey.X1)
  X2Var = prover.AppendElement("X2", serverPublicKey.X2)
  X0AuxVar = prover.AppendElement("X0Aux", X0Aux)
  X1AuxVar = prover.AppendElement("X1Aux", X1Aux)
  X2AuxVar = prover.AppendElement("X2Aux", X2Aux)
  HAuxVar = prover.AppendElement("HAux", HAux)

  # 1. X0 = x0 * generatorG + x0Blinding * generatorH
  prover.Constrain(X0Var, [(x0Var, genGVar), (x0BlindingVar, genHVar)])
  # 2. X1 = x1 * generatorH
  prover.Constrain(X1Var, [(x1Var, genHVar)])
  # 3. X2 = x2 * generatorH
  prover.Constrain(X2Var, [(x2Var, genHVar)])

  # 4. X0Aux = b * x0Blinding * generatorH
  # 4a. HAux = b * generatorH
  prover.Constrain(HAuxVar, [(bVar, genHVar)])
  # 4b: X0Aux = x0Blinding * HAux (= b * x0Blinding * generatorH)
  prover.Constrain(X0AuxVar, [(x0BlindingVar, HAuxVar)])

  # 5. X1Aux = b * x1 * generatorH
  # 5a. X1Aux = t1 * generatorH (t1 = b * x1)
  prover.Constrain(X1AuxVar, [(t1Var, genHVar)])
  # 5b. X1Aux = b * X1 (X1 = x1 * generatorH)
  prover.Constrain(X1AuxVar, [(bVar, X1Var)])

  # 6. X2Aux = b * x2 * generatorH
  # 6a. X2Aux = b * X2 (X2 = x2 * generatorH)
  prover.Constrain(X2AuxVar, [(bVar, X2Var)])
  # 6b. X2Aux = t2 * H (t2 = b * x2)
  prover.Constrain(X2AuxVar, [(t2Var, genHVar)])

  # 7. U = b * generatorG
  prover.Constrain(UVar, [(bVar, genGVar)])
  # 8. encUPrime = b * (X0 + x1 * Enc(m1) + x2 * Enc(m2))
  # simplified: encUPrime = b * X0 + t1 * m1Enc + t2 * m2Enc, since t1 = b * x1 and t2 = b * x2
  prover.Constrain(encUPrimeVar, [(bVar, X0Var), (t1Var, m1EncVar), (t2Var, m2EncVar)])

  return prover.Prove()
]]></artwork>
        </section>
        <section anchor="credentialresponse-proof-verification">
          <name>CredentialResponse Proof Verification</name>
          <artwork><![CDATA[
validity = VerifyCredentialResponseProof(serverPublicKey, response, request)

Inputs:
- serverPublicKey:
  - X0: Element, server public key 0.
  - X1: Element, server public key 1.
  - X2: Element, server public key 2.
- response:
  - U: Element, a randomized generator for the response. `b*G`.
  - encUPrime: Element, encrypted UPrime.
  - X0Aux: Element, auxiliary point for X0.
  - X1Aux: Element, auxiliary point for X1.
  - X2Aux: Element, auxiliary point for X2.
  - HAux: Element, auxiliary point for generatorH.
  - responseProof: ZKProof, a proof of correct generation of U, encUPrime, server public keys, and auxiliary points.
    - challenge: Scalar, the challenge used in the proof of valid response.
    - response0: Scalar, the response corresponding to x0.
    - response1: Scalar, the response corresponding to x1.
    - response2: Scalar, the response corresponding to x2.
    - response3: Scalar, the response corresponding to x0Blinding.
    - response4: Scalar, the response corresponding to b.
    - response5: Scalar, the response corresponding to t1.
    - response6: Scalar, the response corresponding to t2.
- request:
  - m1Enc: Element, first encrypted secret.
  - m2Enc: Element, second encrypted secret.
  - requestProof: ZKProof, a proof of correct generation of m1Enc and m2Enc.

Outputs:
- validity: Boolean, True if the proof verifies correctly, False otherwise.

Parameters:
- G: Group
- generatorG: Element, equivalent to G.GeneratorG()
- generatorH: Element, equivalent to G.GeneratorH()

def VerifyCredentialResponseProof(serverPublicKey, response, request):
  verifier = Verifier(contextString + "CredentialResponse")

  x0Var = verifier.AppendScalar("x0")
  x1Var = verifier.AppendScalar("x1")
  x2Var = verifier.AppendScalar("x2")
  x0BlindingVar = verifier.AppendScalar("x0Blinding")
  bVar = verifier.AppendScalar("b", b)
  t1Var = verifier.AppendScalar("t1")
  t2Var = verifier.AppendScalar("t2")

  genGVar = verifier.AppendElement("genG", generatorG)
  genHVar = verifier.AppendElement("genH", generatorH)
  m1EncVar = verifier.AppendElement("m1Enc", request.m1Enc)
  m2EncVar = verifier.AppendElement("m2Enc", request.m2Enc)
  UVar = verifier.AppendElement("U", response.U)
  encUPrimeVar = verifier.AppendElement("encUPrime", response.encUPrime)
  X0Var = verifier.AppendElement("X0", serverPublicKey.X0)
  X1Var = verifier.AppendElement("X1", serverPublicKey.X1)
  X2Var = verifier.AppendElement("X2", serverPublicKey.X2)
  X0AuxVar = verifier.AppendElement("X0Aux", response.X0Aux)
  X1AuxVar = verifier.AppendElement("X1Aux", response.X1Aux)
  X2AuxVar = verifier.AppendElement("X2Aux", response.X2Aux)
  HAuxVar = verifier.AppendElement("HAux", response.HAux)

  # 1. X0 = x0 * generatorG + x0Blinding * generatorH
  verifier.Constrain(X0Var, [(x0Var, genGVar), (x0BlindingVar, genHVar)])
  # 2. X1 = x1 * generatorH
  verifier.Constrain(X1Var, [(x1Var, genHVar)])
  # 3. X2 = x2 * generatorH
  verifier.Constrain(X2Var, [(x2Var, genHVar)])

  # 4. X0Aux = b * x0Blinding * generatorH
  # 4a. HAux = b * generatorH
  verifier.Constrain(HAuxVar, [(bVar, genHVar)])
  # 4b: X0Aux = x0Blinding * HAux (= b * x0Blinding * generatorH)
  verifier.Constrain(X0AuxVar, [(x0BlindingVar, HAuxVar)])

  # 5. X1Aux = b * x1 * generatorH
  # 5a. X1Aux = t1 * generatorH (t1 = b * x1)
  verifier.Constrain(X1AuxVar, [(t1Var, genHVar)])
  # 5b. X1Aux = b * X1 (X1 = x1 * generatorH)
  verifier.Constrain(X1AuxVar, [(bVar, X1Var)])

  # 6. X2Aux = b * x2 * generatorH
  # 6a. X2Aux = b * X2 (X2 = x2 * generatorH)
  verifier.Constrain(X2AuxVar, [(bVar, X2Var)])
  # 6b. X2Aux = t2 * H (t2 = b * x2)
  verifier.Constrain(X2AuxVar, [(t2Var, genHVar)])

  # 7. U = b * generatorG
  verifier.Constrain(UVar, [(bVar, genGVar)])
  # 8. encUPrime = b * (X0 + x1 * Enc(m1) + x2 * Enc(m2))
  # simplified: encUPrime = b * X0 + t1 * m1Enc + t2 * m2Enc, since t1 = b * x1 and t2 = b * x2
  verifier.Constrain(encUPrimeVar, [(bVar, X0Var), (t1Var, m1EncVar), (t2Var, m2EncVar)])

  return verifier.Verify(response.proof)
]]></artwork>
        </section>
      </section>
      <section anchor="presentation-proof">
        <name>Presentation Proof</name>
        <t>The presentation proof is a proof of knowledge of (m1, r, z) used in the presentation, and a proof that the nonce used to make the tag is in the range of [0, presentationLimit).</t>
        <t>Statements to prove:</t>
        <artwork><![CDATA[
1. m1Commit = m1 * U + z * generatorH
2. V = z * X1 - r * generatorG
3. G.HashToGroup(presentationContext, "Tag") = m1 * tag + nonce * tag
4. m1Tag = m1 * tag
]]></artwork>
        <section anchor="presentation-proof-creation">
          <name>Presentation Proof Creation</name>
          <artwork><![CDATA[
presentationProof = MakePresentationProof(U, UPrimeCommit, m1Commit, tag, generatorT, credential, V, r, z, nonce, m1Tag)

Inputs:
- U: Element, re-randomized from the U in the response.
- UPrimeCommit: Element, a public key to the MACGGM output UPrime.
- m1Commit: Element, a public key to the client secret (m1).
- tag: Element, the tag element used for enforcing the presentation limit.
- generatorT: Element, used for presentation tag computation.
- credential:
  - m1: Scalar, client's first secret.
  - U: Element, a randomized generator for the response. `b*G`.
  - UPrime: Element, the MAC over the server's private keys and the client's secrets.
  - X1: Element, server public key 1.
- V: Element, a proof helper element.
- r: Scalar (private), a randomly generated element used in presentation.
- z: Scalar (private), a randomly generated element used in presentation.
- nonce: Int, the nonce associated with the presentation.
- m1Tag: Element, helper element for the proof.

Outputs:
- proof: ZKProof
  - challenge: Scalar, the challenge used in the proof of valid presentation.
  - response0: Scalar, the response corresponding to m1.
  - response1: Scalar, the response corresponding to z.
  - response2: Scalar, the response corresponding to -r.
  - response3: Scalar, the response corresponding to nonce.

Parameters:
- G: Group
- generatorG: Element, equivalent to G.GeneratorG()
- generatorH: Element, equivalent to G.GeneratorH()
- contextString: public input

def MakePresentationProof(U, UPrimeCommit, m1Commit, tag, generatorT, presentationContext, credential, V, r, z, nonce, m1Tag)
  prover = Prover(contextString + "CredentialPresentation")

  m1Var = prover.AppendScalar("m1", credential.m1)
  zVar = prover.AppendScalar("z", z)
  rNegVar = prover.AppendScalar("-r", -r)
  nonceVar = prover.AppendScalar("nonce", nonce)

  genGVar = prover.AppendElement("genG", generatorG)
  genHVar = prover.AppendElement("genH", generatorH)
  UVar = prover.AppendElement("U", U)
  _ = prover.AppendElement("UPrimeCommit", UPrimeCommit)
  m1CommitVar = prover.AppendElement("m1Commit", m1Commit)
  VVar = prover.AppendElement("V", V)
  X1Var = prover.AppendElement("X1", credential.X1)
  tagVar = prover.AppendElement("tag", tag)
  genTVar = prover.AppendElement("genT", generatorT)
  m1TagVar = prover.AppendElement("m1Tag", m1Tag)

  # 1. m1Commit = m1 * U + z * generatorH
  prover.Constrain(m1CommitVar, [(m1Var, UVar), (zVar, genHVar)])
  # 2. V = z * X1 - r * generatorG
  prover.Constrain(VVar, [(zVar, X1Var), (rNegVar, genGVar)])
  # 3. G.HashToGroup(presentationContext, "Tag") = m1 * tag + nonce * tag
  prover.Constrain(genTVar, [(m1Var, tagVar), (nonceVar, tagVar)])
  # 4. m1Tag = m1 * tag
  prover.Constrain(m1TagVar, [(m1Var, tagVar)])

  return prover.Prove()
]]></artwork>
        </section>
        <section anchor="presentation-proof-verification">
          <name>Presentation Proof Verification</name>
          <artwork><![CDATA[
validity = VerifyPresentationProof(
  serverPrivateKey,
  serverPublicKey,
  requestContext,
  presentationContext,
  presentation,
  m1Tag)

Inputs:
- serverPrivateKey:
  - x0: Scalar (private), server private key 0.
  - x1: Scalar (private), server private key 1.
  - x2: Scalar (private), server private key 2.
  - x0Blinding: Scalar (private), blinding value for x0.
- serverPublicKey:
  - X0: Element, server public key 0.
  - X1: Element, server public key 1.
  - X2: Element, server public key 2.
- requestContext: Data, context for the credential request.
- presentationContext: Data (public), used for presentation tag computation.
- presentation:
  - U: Element, re-randomized from the U in the response.
  - UPrimeCommit: Element, a public key to the issued UPrime.
  - m1Commit: Element, a public key to the client secret (m1).
  - tag: Element, the tag element used for enforcing the presentation limit.
  - presentationProof: ZKProof, a proof of correct generation of the presentation.
    - challenge: Scalar, the challenge used in the proof of valid presentation.
    - response0: Scalar, the response corresponding to m1.
    - response1: Scalar, the response corresponding to z.
    - response2: Scalar, the response corresponding to -r.
    - response3: Scalar, the response corresponding to nonce.
- m1Tag: Element, helper to validate the presentation proof.

Outputs:
- validity: Boolean, True if the proof verifies correctly, False otherwise.

Parameters:
- G: Group
- generatorG: Element, equivalent to G.GeneratorG()
- generatorH: Element, equivalent to G.GeneratorH()
- contextString: public input

def VerifyPresentationProof(
  serverPrivateKey,
  serverPublicKey,
  requestContext,
  presentationContext,
  presentation,
  m1Tag):

  m2 = G.HashToScalar(requestContext, "requestContext")
  V = serverPrivateKey.x0 * presentation.U + serverPrivateKey.x1 * presentation.m1Commit + serverPrivateKey.x2 * m2 * presentation.U - presentation.UPrimeCommit
  generatorT = G.HashToGroup(presentationContext, "Tag")

  verifier = Verifier(contextString + "CredentialPresentation")

  m1Var = verifier.AppendScalar("m1")
  zVar = verifier.AppendScalar("z")
  rNegVar = verifier.AppendScalar("-r")
  nonceVar = verifier.AppendScalar("nonce")

  genGVar = verifier.AppendElement("genG", generatorG)
  genHVar = verifier.AppendElement("genH", generatorH)
  UVar = verifier.AppendElement("U", presentation.U)
  _ = verifier.AppendElement("UPrimeCommit", presentation.UPrimeCommit)
  m1CommitVar = verifier.AppendElement("m1Commit", presentation.m1Commit)
  VVar = verifier.AppendElement("V", presentation.V)
  X1Var = verifier.AppendElement("X1", serverPublicKey.X1)
  tagVar = prover.AppendElement("tag", presentation.tag)
  genTVar = verifier.AppendElement("genT", generatorT)
  m1TagVar = prover.AppendElement("m1Tag", m1Tag)

  # 1. m1Commit = m1 * U + z * generatorH
  verifier.Constrain(m1CommitVar, [(m1Var, UVar), (zVar, genHVar)])
  # 2. V = z * X1 - r * generatorG
  verifier.Constrain(VVar, [(zVar, X1Var), (rNegVar, genGVar)])
  # 3. G.HashToGroup(presentationContext, "Tag") = m1 * tag + nonceVar * tag
  verifier.Constrain(genTVar, [(m1Var, tagVar), (nonceVar, tagVar)])
  # 4. m1Tag = m1 * tag
  prover.Constrain(m1TagVar, [(m1Var, tagVar)])

  return verifier.Verify(presentation.proof)
]]></artwork>
        </section>
      </section>
      <section anchor="range-proof-for-arbitrary-values">
        <name>Range Proof for Arbitrary Values</name>
        <t>This section specifies a range proof in the framework of
<xref target="SIGMA"/> to prove a secret value <tt>v</tt> lies
in an arbitrary interval <tt>[0,upper_bound)</tt>. Before specifying the proof system, we first
give a brief overview of how it works. For simplicity, assume that <tt>upper_bound</tt> is a
power of two, that is, <tt>upper_bound == 2^k</tt> for some <tt>k</tt>.</t>
        <t>To prove a value lies in <tt>[0,(2^k)-1)</tt>, we prove it has a valid <tt>k</tt>-bit representation.
This is proven by committing to the full value <tt>v</tt>, then all bits of the bit decomposition
<tt>b</tt> of the value <tt>v</tt>, and then proving each coefficient of the bit decomposition is
actually <tt>0</tt> or <tt>1</tt> and that the sum of the bits amounts to the full value <tt>v</tt>.  This involves the following steps:</t>
        <ol spacing="normal" type="1"><li>
            <t>Commit to the bits of <tt>v</tt>. That is, for each bit <tt>b[i]</tt> of the bit decomposition of <tt>v</tt>, let
<tt>D[i] = b[i] * generatorG + s[i] * generatorH</tt>, where <tt>s[i]</tt> is a blinding scalar.</t>
          </li>
          <li>
            <t>Prove that <tt>b[i]</tt> is in <tt>{0,1}</tt> by computing proving the algebraic relation <tt>b[i] *
(b[i]-1) == 0</tt> holds. This quadratic relation can be linearized by
adding an auxilary witness <tt>s2[i]</tt> and adding the linear relation
<tt>D[i] == b[i] * D[i] + s2[i] * generatorH</tt> to the equation system. A valid witness <tt>s2[i]</tt> can only
be computed by the prover if <tt>b[i]</tt> is in <tt>{0,1}</tt>. Successfully computing a witness for
any other value requires the prover to break the discrete logarithm problem.</t>
          </li>
          <li>
            <t>Having verified the proof the above relation, the verifier checks the sum by computing</t>
          </li>
        </ol>
        <artwork><![CDATA[
C == D[0] * 2^0 + D[1] * 2^1 + D[2] * 2^2 + ... + D[k-1] * 2^{k-1}
]]></artwork>
        <t>The third step is verified outside of the proof by adding the commitments
homomorphically.</t>
        <t>To support the general case, where <tt>upper_bound</tt> is not necessarily a power of two,
we extend the range proof for arbitrary ranges by decomposing the range
up to the second highest power of two and adding an additional, non-binary range that
covers the remaining range. This is detailed in <tt>ComputeBases</tt> below.</t>
        <artwork><![CDATA[
def ComputeBases(upper_bound):

Inputs:

- upper_bound: the maximum value of the range (exclusive), as integer.

Outputs:

- bases: an array of Scalar bases to represent elements, sorted in descending order. A base is
  either a power of two or a unique remainder that can be used to represent any integer
  in [0, upper_bound).

# compute bases to express the commitment as a linear combination of the bit decomposition
remainder=upper_bound
bases=[]
# Generate all but the last power-of-two base.
for i in range(ceil(log2(upper_bound)) - 1):
    base = 2 ** i
    remainder -= base
    bases.append((G.Scalar(base))
bases.append(remainder - 1)

# call sorted on array to ensure the additional base is in correct order
return sorted(bases, reverse=True)
]]></artwork>
        <t>Using the bases from <tt>ComputeBases</tt>, the function <tt>ComputeStatementAndWitnesses</tt>
represents the secret value <tt>v</tt> as a linear combination of the bases, using the resulting
bit representation to generate the cryptographic commitments and witness values for the
range proof.</t>
        <artwork><![CDATA[
def ComputeStatementAndWitnesses(v, upper_bound):

Inputs:

- v: the scalar we want to prove is in range [0, upper_bound)
- r: randomness for commitment to v
- upper_bound: the maximum integer value of the range

Outputs:

- statement: proof statement for the relation
- [s,s2]: the witness for the equations appended to the statement (the bit
  decomposition, the secret shares of r, and the auxiliary witness s2. Each s2[i] is either zero when
b[i] is set) or s[i] when b[i] is zero.
- C: the commitment to v
- D: the commitments to the bit decomposition of v

Parameters:
- G: group
- generatorG: Element, equivalent to G.GeneratorG()
- generatorH: Element, equivalent to G.GeneratorH()

Exceptions:
- NumberTooBigError, raised when v is out of range

if G.ScalarToInt(v) >= upper_bound:
    raise NumberTooBigError

bases = ComputeBases(upper_bound)

# Compute bit decomposition of v.
b = []
v_remainder = G.ScalarToInt(v)
for base in bases:
    # Implementation note: In order to avoid leaking v via a timing channel, this code should be written to be constant time.
    if v_remainder >= base:
        v_remainder -= G.ScalarToInt(base)
        b.append(G.Scalar(1))
    else:
        b.append(G.Scalar(0))

# array of group elements where the i-th element corresponds to b[i] * generatorG + s * generatorH
D = []
# blinding elements for Pedersen commitment, secret shares of r
s = []
# complementing blinders for proof of bit-ness
s2 = []
partial_sum = G.Scalar(0)
for i in range(len(bases) - 1):
    s.append(G.random_scalar())
    partial_sum += bases[i] * s[i]
    s2.append((G.Scalar(1) - b[i]) * s[i])
    D.append(b[i] * generatorG + s[i] * generatorH)
idx = len(bases) - 1
s[idx] = r - partial_sum
s2.append((G.Scalar(1) - b[idx]) * s[idx])
D.append(b[idx] * generatorG + s[idx] * generatorH)

# Compute the Pedersen commitment to the full value of v, using the provided r.
C = v * generatorG + r * generatorH

# start computing the linear relation
statement = LinearRelation(G)

[var_G, var_H, var_C] = statement.allocate_elements(3)

# allocate variables for decomposed statements
vars_b = statement.allocate_scalars(len(b))
# allocate blinding elements for Pedersen commitment
vars_s = statement.allocateScalars(len(b))
# allocate complementing blinders for proof of bit-ness
vars_s2 = statement.allocateScalars(len(b))
# allocate bit commitment values
vars_D = statement.allocateElements(len(b))

# Add equations proving each b[i] is in {0,1}
# For each base, we prove:
#   D[i] = b[i] * generatorG + s[i] * generatorH      (b[i] is committed in D[i])
#   b[i] * (b[i] - 1) = 0       (b[i] is 0 or 1)
for i in range(len(b)):
        statement.set_elements([(vars_D[i], D[i])])
        # add Pedersen commitment to the ith bit.
        statement.append_equation(vars_D[i], [(vars_b[i], var_G), (vars_s[i], var_H)])
        # add statement that b[i] is in {0,1}
        statement.append_equation(vars_D[i], [(vars_b[i], vars_D[i]), (vars_s2[i], var_H)])

return (statement, [r, v, b, s, s2], [C,D])
]]></artwork>
      </section>
    </section>
    <section anchor="ciphersuites">
      <name>Ciphersuites</name>
      <t>A ciphersuite (also referred to as 'suite' in this document) for the protocol
wraps the functionality required for the protocol to take place. The
ciphersuite should be available to both the client and server, and agreement
on the specific instantiation is assumed throughout.</t>
      <t>A ciphersuite contains an instantiation of the following functionality:</t>
      <ul spacing="normal">
        <li>
          <t><tt>Group</tt>: A prime-order Group exposing the API detailed in <xref target="pog"/>, with the
generator element defined in the corresponding reference for each group. Each
group also specifies HashToGroup, HashToScalar, and serialization functionalities.
For HashToGroup, the domain separation tag (DST) is constructed in accordance
with the recommendations in <xref section="3.1" sectionFormat="comma" target="I-D.irtf-cfrg-hash-to-curve"/>.
For HashToScalar, each group specifies an integer order that is used in
reducing integer values to a member of the corresponding scalar field.</t>
        </li>
      </ul>
      <t>This section includes an initial set of ciphersuites with supported groups.
It also includes implementation details for each ciphersuite, focusing on input validation.</t>
      <section anchor="arcp-256">
        <name>ARC(P-256)</name>
        <t>This ciphersuite uses P-256 <xref target="NISTCurves"/> for the Group.
The value of the ciphersuite identifier is "P256". The value of
contextString is "ARCV1-P256".</t>
        <ul spacing="normal">
          <li>
            <t>Group: P-256 (secp256r1) <xref target="NISTCurves"/>
            </t>
            <ul spacing="normal">
              <li>
                <t>Order(): Return 0xffffffff00000000ffffffffffffffffbce6faada7179e84f3b9cac2fc632551.</t>
              </li>
              <li>
                <t>Identity(): As defined in <xref target="NISTCurves"/>.</t>
              </li>
              <li>
                <t>Generator(): As defined in <xref target="NISTCurves"/>.</t>
              </li>
              <li>
                <t>RandomScalar(): Implemented by returning a uniformly random Scalar in the range
[1, <tt>G.Order()</tt> - 1]. Refer to <xref target="random-scalar"/> for implementation guidance.</t>
              </li>
              <li>
                <t>HashToGroup(x, info): Use hash_to_curve with suite P256_XMD:SHA-256_SSWU_RO_
<xref target="I-D.irtf-cfrg-hash-to-curve"/>, input <tt>x</tt>, and DST =
"HashToGroup-" || contextString || info.</t>
              </li>
              <li>
                <t>HashToScalar(x, info): Use hash_to_field from <xref target="I-D.irtf-cfrg-hash-to-curve"/>
using L = 48, <tt>expand_message_xmd</tt> with SHA-256, input <tt>x</tt> and
DST = "HashToScalar-" || contextString || info, and
prime modulus equal to <tt>Group.Order()</tt>.</t>
              </li>
              <li>
                <t>ScalarInverse(s): Returns the multiplicative inverse of input Scalar <tt>s</tt> mod <tt>Group.Order()</tt>.</t>
              </li>
              <li>
                <t>SerializeElement(A): Implemented using the compressed Elliptic-Curve-Point-to-Octet-String
method according to <xref target="SEC1"/>; Ne = 33.</t>
              </li>
              <li>
                <t>DeserializeElement(buf): Implemented by attempting to deserialize a 33-byte array to
a public key using the compressed Octet-String-to-Elliptic-Curve-Point method according to <xref target="SEC1"/>,
and then performs partial public-key validation as defined in section 5.6.2.3.4 of
<xref target="KEYAGREEMENT"/>. This includes checking that the
coordinates of the resulting point are in the correct range, that the point is on
the curve, and that the point is not the point at infinity. Additionally, this function
validates that the resulting element is not the group identity element.
If these checks fail, deserialization returns an InputValidationError error.</t>
              </li>
              <li>
                <t>SerializeScalar(s): Implemented using the Field-Element-to-Octet-String conversion
according to <xref target="SEC1"/>; Ns = 32.</t>
              </li>
              <li>
                <t>DeserializeScalar(buf): Implemented by attempting to deserialize a Scalar from a 32-byte
string using Octet-String-to-Field-Element from <xref target="SEC1"/>. This function can fail if the
input does not represent a Scalar in the range [0, <tt>G.Order()</tt> - 1].</t>
              </li>
            </ul>
          </li>
        </ul>
      </section>
      <section anchor="random-scalar">
        <name>Random Scalar Generation</name>
        <t>Two popular algorithms for generating a random integer uniformly distributed in
the range [1, G.Order() -1] are as follows:</t>
        <section anchor="rejection-sampling">
          <name>Rejection Sampling</name>
          <t>Generate a random byte array with <tt>Ns</tt> bytes, and attempt to map to a Scalar
by calling <tt>DeserializeScalar</tt> in constant time. If it succeeds and is non-zero,
return the result. Otherwise, try again with another random byte array, until the
procedure succeeds. Failure to implement <tt>DeserializeScalar</tt> in constant time
can leak information about the underlying corresponding Scalar.</t>
          <t>As an optimization, if the group order is very close to a power of
2, it is acceptable to omit the rejection test completely.  In
particular, if the group order is p, and there is an integer b
such that |p - 2<sup>b</sup>| is less than 2<sup>(b/2)</sup>, then
<tt>RandomScalar</tt> can simply return a uniformly random integer of at
most b bits.</t>
        </section>
        <section anchor="random-number-generation-using-extra-random-bits">
          <name>Random Number Generation Using Extra Random Bits</name>
          <t>Generate a random byte array with <tt>L = ceil(((3 * ceil(log2(G.Order()))) / 2) / 8)</tt>
bytes, and interpret it as an integer; reduce the integer modulo <tt>G.Order()</tt> and return the
result. See <xref section="5" sectionFormat="comma" target="I-D.irtf-cfrg-hash-to-curve"/> for the underlying derivation of <tt>L</tt>.</t>
        </section>
      </section>
    </section>
    <section anchor="security-considerations">
      <name>Security Considerations</name>
      <t>For arguments about correctness, unforgeability, anonymity, and blind issuance of the ARC protocol, see the
"Formal Security Definitions for Keyed-Verification Anonymous Credentials" in <xref target="KVAC"/>.</t>
      <t>This section elaborates on unlinkability properties for ARC and other implementation details
necessary for these properties to hold.</t>
      <section anchor="credential-request-unlinkability">
        <name>Credential Request Unlinkability</name>
        <t>Client credential requests are constructed such that the server cannot distinguish between any two credential requests from the same client and two requests from different clients. We refer to this property as issuance unlinkability. This property is achieved by the way the credential requests are constructed. In particular, each credential request consists of two Pedersen commitments with fresh blinding factors, which are used to commit to a freshly generated client secret and request context. The resulting request is therefore statistically hiding, and independent from other requests from the same client. More details about this unlinkability property can be found in <xref target="KVAC"/> and <xref target="REVISITING_KVAC"/>.</t>
      </section>
      <section anchor="credential-issuance-unlinkability">
        <name>Credential Issuance Unlinkability</name>
        <t>The server commitment to <tt>x0</tt> is defined as <tt>X0 = x0 * G.generatorG() + x0Blinding * G.GeneratorH()</tt>, following the definitions in <xref target="KVAC"/>. This is computationally binding to the secret key <tt>x0</tt>. This means that unless the discrete log is broken, the credentials issued under one server commitment <tt>X0, X1, ...</tt> will all be issued under the same private keys <tt>x0, x1, ...</tt></t>
        <t>However, an adversary breaking the discrete log (e.g., a quantum adversary) can find pairs <tt>(x0, x0Blinding)</tt> and <tt>(x0', x0Blinding')</tt> both committing to <tt>X0</tt> and use them to issue different credentials. This capability would let the adversary partitioning the client anonymity set by linking clients to the underlying secret used for credential issuance, i.e., <tt>x0</tt> or <tt>x0'</tt>. This requires an active attack and therefore is not an immediate concern.</t>
        <t>Statistical anonymity is possible by committing to <tt>x0</tt> and x0Blinding` separately, as in <xref target="REVISITING_KVAC"/>. However, the security of this construction requires additional analysis.</t>
      </section>
      <section anchor="pres-unlinkability">
        <name>Presentation Unlinkability</name>
        <t>Client credential presentations are constructed so that all presentations are indistinguishable, even if coming from the same user. We refer to this property as presentation unlinkability. This property is achieved by the way the credential presentations are constructed. The presentation elements <tt>[U, UPrimeCommit, m1Commit]</tt> are indistinguishable from all other presentations made from credentials issued with the same server keys, as detailed in <xref target="KVAC"/>.</t>
        <t>The indistinguishability set for these presentation elements is <tt>sum_{i=0}^c(p_i)</tt>, where <tt>c</tt> is the number of credentials issued with the same server keys, and <tt>p_i</tt> is the number of presentations made for each of those credentials.</t>
        <t>The presentation elements <tt>[tag, nonce, presentationContext, presentationProof]</tt> are indistinguishable from all presentations made from credentials issued with the same server keys for that presentationContext, with the exception of presentations with the same nonce (since those presentations can be ascertained as being generated from different credentials, as long as the presentation tag is unique).</t>
        <t>The indistinguishability set for those presentation elements is <tt>sum_{i=0}^c(p_i[presentationContext]) - k[presentationContext]</tt>, where <tt>c</tt> is the number of credentials issued with the same server keys, <tt>p_i[presentationContext]</tt> is the number of presentations made for each of those credentials with the same presentationContext, and <tt>k</tt> is the number of presentations with the same nonce for that presentationContext. As long as the nonces are generated randomly from the range defined by the presentation limit, <tt>k[presentationContext]</tt> should be roughly equal to <tt>sum_{i=0}^c(p_i[presentationContext]) / n</tt>, where <tt>n</tt> is the presentation limit. Therefore, the indistinguishability set can be represented as <tt>sum_{i=0}^c(p_i[presentationContext])(1 - 1/n)</tt>, where a larger presentation limit results in a larger indistinguishability set and therefore stronger unlinkability properties.</t>
        <t>OPEN ISSUE: hide the nonce and replace the tag proof with a range proof built from something like Bulletproofs.</t>
      </section>
      <section anchor="timing-leaks">
        <name>Timing Leaks</name>
        <t>To ensure no information is leaked during protocol execution, all operations that use secret data MUST run in constant time. This includes all prime-order group operations and proof-specific operations that operate on secret data, including proof generation and verification.</t>
      </section>
    </section>
    <section anchor="alternatives-considered">
      <name>Alternatives considered</name>
      <t>ARC uses the MACGGM algebraic MAC as its underlying primitive, as detailed in <xref target="KVAC"/> and <xref target="REVISITING_KVAC"/>. This offers the benefit of having a lower credential size than MACDDH, which is an alternative algebraic MAC detailed in <xref target="KVAC"/>.</t>
      <t>The BBS anonymous credential scheme, as detailed in <xref target="BBS"/> and its variants, is efficient and publicly verifiable, but requires pairings for verification. This is problematic for adoption because pairings are not supported as widely in software and hardware as non-pairing elliptic curves.</t>
      <t>It is possible to construct a keyed-verification variant of BBS which doesn't use pairings, as discussed in <xref target="BBDT17"/> and <xref target="REVISITING_KVAC"/>. However these keyed-verification BBS variants require more analysis, proofs of security properties, and review to be considered mature enough for safe deployment.</t>
    </section>
    <section anchor="iana-considerations">
      <name>IANA Considerations</name>
      <t>This document has no IANA actions.</t>
    </section>
    <section anchor="test-vectors">
      <name>Test Vectors</name>
      <t>This section contains test vectors for the ARC ciphersuites specified in this document.</t>
      <section anchor="arcv1-p256">
        <name>ARCV1-P256</name>
        <artwork><![CDATA[
// ServerKey
x0 = 3338fa65ec36e0290022b48eb562889d89dbfa691d1cde91517fa222ed7ad36
4
x1 = f9db001266677f62c095021db018cd8cbb55941d4073698ce45c405d1348b7b
1
x2 = 350e8040f828bf6ceca27405420cdf3d63cb3aef005f40ba51943c802687796
3
xb = fd293126bb49a6d793cd77d7db960f5692fec3b7ec07602c60cd32aee595dff
d
X0 = 0232b5e93dc2ff489c20a986a84757c5cc4512f057e1ea92011a26d3ad2c562
88d
X1 = 03c413230a9bd956718aa46138a33f774f4c708d61c1d6400d404243049d4a3
1dc
X2 = 02db00f6f8e6d235786a120017bd356fe1c9d09069d3ac9352cc9be10ef1505
a55

// CredentialRequest
Blinding_0 = 0000000000000000000000000000000000000000000000000000000
000000001
Blinding_1 = 0000000000000000000000000000000000000000000000000000000
000000002
Blinding_2 = 0000000000000000000000000000000000000000000000000000000
000000003
Blinding_3 = 0000000000000000000000000000000000000000000000000000000
000000004
request_context = 74657374207265717565737420636f6e74657874
m1 = eedfe7939e2382934ab5b0f76aae44124955d2c5ebf9b41d88786259c34692d
2
m2 = 911fb315257d9ae29d47ecb48c6fa27074dee6860a0489f8db6ac9a486be6a3
e
r1 = 008035081690bfde3b1e68b91443c22cc791d244340fe957d5aa44d7313740d
f
r2 = d59c5e6ff560cc597c2b8ca25256c720bceca2ab03921492c5e9e4ad3b55800
2
m1_enc = 03b8f11506a5302424143573e087fa20195cb5e893a67ef354eae3a78e2
63c54e4
m2_enc = 03f1ae4d7b78ba8030bd63859d4f4a909395c52bda34716b6620a2fdd52
b336fc9
proof = 0f361327abbc724ff0d37db365065bc4bd60e18125842bb4c03a7e5a632a
1e95e74dcc440fcb9fb39106922e0d2544e6c82ca710abf35e8b10bf5d61296c9adb
7d683eaed9a76a755b73f2b4b6e763a7c7883ce4b5c21bd02cd96b9af18cfb227f1a
cb4ead77c85049d291ed7841405610843f163e9cc2f6a8869111582324cd32bf1300
0c129d274ccf5386cb90e839916d5dff7eade18e3eabec415f613911

// CredentialResponse
Blinding_0 = 0000000000000000000000000000000000000000000000000000000
000000001
Blinding_1 = 0000000000000000000000000000000000000000000000000000000
000000002
Blinding_2 = 0000000000000000000000000000000000000000000000000000000
000000003
Blinding_3 = 0000000000000000000000000000000000000000000000000000000
000000004
Blinding_4 = 0000000000000000000000000000000000000000000000000000000
000000005
Blinding_5 = 0000000000000000000000000000000000000000000000000000000
000000006
Blinding_6 = 0000000000000000000000000000000000000000000000000000000
000000007
b = e699140babbe599f7dd8f6e3e8f615e5f201d1c2b0bc2f821f19e80a0a0e1e7b
U = 033ee1ebbcff622bc26b10932ed1eb147226d832048fb2337dc0ad7722cb0748
3d
enc_U_prime = 035b8e09ce8776f1a2c7ef8610c9a6a39936c5666ab8b28d6629d3
685056716482
X0_aux = 02d453c121324114367906bd11ffc3b6e6a77b75382497279b1a60ab841
2c1dec6
X1_aux = 03b0e4b1f376c6207bf34efda46ce54b132a20b90bc28b9152f3e441fe2
b508b63
X2_aux = 0327369efcb7577abaeb7b56940e6e042126900bdf8bd8944c0adbb7be3
ad98e2a
H_aux = 03d3cd09eeb8d19716586a49260c69309c495a717a36cad3381f6c02ac80
b70e64
proof = dd4596175db0b4273fcdff330370d2b5e7a4bf92bf518141f4553af37ef0
e1260cb8312affc2462800adba102117448b449985d1704d8afd0df9ac708231561d
ca56faae325cb56b0a9e8ad07bdc6ce90f6e7430090e970a7240e289218de7a17672
bea9a66187d102ffef976fb01af69d8d3aa3156a5a4223dc6d08b8ce9f1d2639a2ed
c7052404bf1410adf6c41465bd687e3dfa5372ea71f804b56d947bae9482e5707f42
dbe35f8b0e11b4a0d27a5a01e1b9a75b66d82b7945eb0b002ee400bebcdc4c3133f8
04b22bd2d771762058cc35a5033365d2e15150fe46d3b0e98e18ee55f0451b0b1714
20f73592292e4ff50603c1f0d7769dbd090936090f63

// Credential
m1 = eedfe7939e2382934ab5b0f76aae44124955d2c5ebf9b41d88786259c34692d
2
U = 033ee1ebbcff622bc26b10932ed1eb147226d832048fb2337dc0ad7722cb0748
3d
U_prime = 02637fe04cc143281ee607bd8f898e670293dce44a2840b9cbb9e0d1fc
7a2b29b4
X1 = 03c413230a9bd956718aa46138a33f774f4c708d61c1d6400d404243049d4a3
1dc

// Presentation1
Blinding_0 = 0000000000000000000000000000000000000000000000000000000
000000001
Blinding_1 = 0000000000000000000000000000000000000000000000000000000
000000002
Blinding_2 = 0000000000000000000000000000000000000000000000000000000
000000003
Blinding_3 = 0000000000000000000000000000000000000000000000000000000
000000004
presentation_context = 746573742070726573656e746174696f6e20636f6e746
57874
a = b78e57df8f0a95d102ff12bbb97e15ed35c23e54f9b4483d30b76772ee60d886
r = 42252210dd60ddbbf1a57e3b144e26dd693b7644a9626a8c36896ede53d12930
z = f5a4bbcf14e55e357df9f5ccb5ded37b2b14bc2e1a68e31f86416f0606ee75d1
U = 032704f22133d2ec70f9e6f4bbf64c582220b666f2e2c1d37c3f8995a2a5568c
7e
U_prime_commit = 03533cf1b2fd53a0716e02425eb42e4c55835aa6b2992d364cb
a70810d0f8aeb51
m1_commit = 03e412408579105213ed10b6447c85bcd672ba73ecae1e21c463d0df
4ef7beb814
nonce = 0x0
tag = 031a774fd87a8f18f6420bea43cf5425e7426eec8ba7b8df5c13dc05f10ec6
52d9
proof = a558da5f17c04adcb0898827aaded14be1dc612dcd12b0579c11bb387ce9
ae4b7dbcb3bbe413caaaf754d99e5a342abb7e0041458d670f4b58eda37e745a6752
95d7a7b86248141d6547b53d793e5c77896ec4dc8dd438ab66d9c8b43ef6b060938a
1ca793057b154970ebc3c7ec3a23134e0852d0041f9098ce77311e5b5eca00000000
00000000000000000000000000000000000000000000000000000004

// Presentation2
Blinding_0 = 0000000000000000000000000000000000000000000000000000000
000000001
Blinding_1 = 0000000000000000000000000000000000000000000000000000000
000000002
Blinding_2 = 0000000000000000000000000000000000000000000000000000000
000000003
Blinding_3 = 0000000000000000000000000000000000000000000000000000000
000000004
presentation_context = 746573742070726573656e746174696f6e20636f6e746
57874
a = 95bcf45150a61f5c44a6cfbf343cd9e0f593f127b6f49bec0f9b20f0550504a2
r = d7ed72750b6d366ed0febdc539b52d89434f468a578c59d7ca9015b7da240ad6
z = 91eedfb168c556ff5ca3b89d047f482c9279b47f584aab6c7f895f7674251771
U = 035fd233dee2c147155c6008ea64941b6ff7b315aced12531468f2e27bf22e3e
f0
U_prime_commit = 02434af337b87fd21d1e3d950aebfc8033a3d2e9dd2bb8b9e79
53488078754496d
m1_commit = 02a578fd3a84eb5b657367b02de39b45fd48ab7781ef8f94efe60127
4a5ded2a07
nonce = 0x1
tag = 03084fe6fff0ecc7c33ef5c49b492dda38083f52e9a2b70b88f3d4b4ba7b50
afba
proof = 050965cde906fc4723333b100ce0fd9f7b026315f1db16984d4cccb2bc4a
a65eb7a17f5b8dfe4f14d40006506ee5fb323e829dd4cb9dc3c455b2e04dd691600a
ec3cc3f1939198a80acb78b7f90b3bff769cab890f33e4d69b7c302d21ad35ec457d
048d3ed7d13ee82c3c0aac2129ad0c8375cf29cd8ea3948a16b9247b1cc5faf69a31
16f903b9dcccc4eff31f026041e49797b53c87eca66cfe1040187ef7

]]></artwork>
      </section>
    </section>
    <section anchor="acknowledgments">
      <name>Acknowledgments</name>
      <t>The authors would like to acknowledge helpful conversations with Tommy Pauly about rate limiting and Privacy Pass integration.</t>
    </section>
  </middle>
  <back>
    <references anchor="sec-combined-references">
      <name>References</name>
      <references anchor="sec-normative-references">
        <name>Normative References</name>
        <reference anchor="RFC8017">
          <front>
            <title>PKCS #1: RSA Cryptography Specifications Version 2.2</title>
            <author fullname="K. Moriarty" initials="K." role="editor" surname="Moriarty"/>
            <author fullname="B. Kaliski" initials="B." surname="Kaliski"/>
            <author fullname="J. Jonsson" initials="J." surname="Jonsson"/>
            <author fullname="A. Rusch" initials="A." surname="Rusch"/>
            <date month="November" year="2016"/>
            <abstract>
              <t>This document provides recommendations for the implementation of public-key cryptography based on the RSA algorithm, covering cryptographic primitives, encryption schemes, signature schemes with appendix, and ASN.1 syntax for representing keys and for identifying the schemes.</t>
              <t>This document represents a republication of PKCS #1 v2.2 from RSA Laboratories' Public-Key Cryptography Standards (PKCS) series. By publishing this RFC, change control is transferred to the IETF.</t>
              <t>This document also obsoletes RFC 3447.</t>
            </abstract>
          </front>
          <seriesInfo name="RFC" value="8017"/>
          <seriesInfo name="DOI" value="10.17487/RFC8017"/>
        </reference>
        <reference anchor="I-D.irtf-cfrg-hash-to-curve">
          <front>
            <title>Hashing to Elliptic Curves</title>
            <author fullname="Armando Faz-Hernandez" initials="A. F." surname="Faz-Hernandez">
              <organization>Cloudflare, Inc.</organization>
            </author>
            <author fullname="Sam Scott" initials="S." surname="Scott">
              <organization>Cornell Tech</organization>
            </author>
            <author fullname="Nick Sullivan" initials="N." surname="Sullivan">
              <organization>Cloudflare, Inc.</organization>
            </author>
            <author fullname="Riad S. Wahby" initials="R. S." surname="Wahby">
              <organization>Stanford University</organization>
            </author>
            <author fullname="Christopher A. Wood" initials="C. A." surname="Wood">
              <organization>Cloudflare, Inc.</organization>
            </author>
            <date day="15" month="June" year="2022"/>
            <abstract>
              <t>This document specifies a number of algorithms for encoding or hashing an arbitrary string to a point on an elliptic curve.  This document is a product of the Crypto Forum Research Group (CFRG) in the IRTF.
              </t>
            </abstract>
          </front>
          <seriesInfo name="Internet-Draft" value="draft-irtf-cfrg-hash-to-curve-16"/>
        </reference>
        <reference anchor="SIGMA">
          <front>
            <title>Interactive Sigma Proofs</title>
            <author fullname="Michele Orrù" initials="M." surname="Orrù">
              <organization>CNRS</organization>
            </author>
            <author fullname="Cathie Yun" initials="C." surname="Yun">
              <organization>Apple, Inc.</organization>
            </author>
            <date day="8" month="August" year="2025"/>
            <abstract>
              <t>   This document describes interactive sigma protocols, a class of
   secure, general-purpose zero-knowledge proofs of knowledge consisting
   of three moves: commitment, challenge, and response.  Concretely, the
   protocol allows one to prove knowledge of a secret witness without
   revealing any information about it.

              </t>
            </abstract>
          </front>
          <seriesInfo name="Internet-Draft" value="draft-irtf-cfrg-sigma-protocols-00"/>
        </reference>
        <reference anchor="KEYAGREEMENT">
          <front>
            <title>Recommendation for pair-wise key-establishment schemes using discrete logarithm cryptography</title>
            <author fullname="Elaine Barker" initials="E." surname="Barker">
              <organization/>
            </author>
            <author fullname="Lily Chen" initials="L." surname="Chen">
              <organization/>
            </author>
            <author fullname="Allen Roginsky" initials="A." surname="Roginsky">
              <organization/>
            </author>
            <author fullname="Apostol Vassilev" initials="A." surname="Vassilev">
              <organization/>
            </author>
            <author fullname="Richard Davis" initials="R." surname="Davis">
              <organization/>
            </author>
            <date month="April" year="2018"/>
          </front>
          <seriesInfo name="DOI" value="10.6028/nist.sp.800-56ar3"/>
          <refcontent>National Institute of Standards and Technology</refcontent>
        </reference>
      </references>
      <references anchor="sec-informative-references">
        <name>Informative References</name>
        <reference anchor="KVAC" target="https://eprint.iacr.org/2013/516">
          <front>
            <title>Keyed-Verification Anonymous Credentials from Algebraic MACs</title>
            <author>
              <organization/>
            </author>
            <date>n.d.</date>
          </front>
        </reference>
        <reference anchor="REVISITING_KVAC" target="https://eprint.iacr.org/2024/1552">
          <front>
            <title>Revisiting Keyed-Verification Anonymous Credentials</title>
            <author>
              <organization/>
            </author>
            <date>n.d.</date>
          </front>
        </reference>
        <reference anchor="BBS" target="https://eprint.iacr.org/2004/174">
          <front>
            <title>Short Group Signatures</title>
            <author>
              <organization/>
            </author>
            <date>n.d.</date>
          </front>
        </reference>
        <reference anchor="BBDT17" target="https://link.springer.com/chapter/10.1007/978-3-319-69453-5_20">
          <front>
            <title>Improved Algebraic MACs and Practical Keyed-Verification Anonymous Credentials</title>
            <author>
              <organization/>
            </author>
            <date>n.d.</date>
          </front>
        </reference>
        <reference anchor="NISTCurves">
          <front>
            <title>Digital Signature Standard (DSS)</title>
            <author>
              <organization/>
            </author>
            <date month="February" year="2023"/>
          </front>
          <seriesInfo name="DOI" value="10.6028/nist.fips.186-5"/>
          <refcontent>National Institute of Standards and Technology (U.S.)</refcontent>
        </reference>
        <reference anchor="SEC1" target="https://www.secg.org/sec1-v2.pdf">
          <front>
            <title>SEC 1: Elliptic Curve Cryptography</title>
            <author initials="" surname="Standards for Efficient Cryptography Group (SECG)">
              <organization/>
            </author>
            <date/>
          </front>
        </reference>
        <reference anchor="BLIND-RSA">
          <front>
            <title>RSA Blind Signatures</title>
            <author fullname="F. Denis" initials="F." surname="Denis"/>
            <author fullname="F. Jacobs" initials="F." surname="Jacobs"/>
            <author fullname="C. A. Wood" initials="C. A." surname="Wood"/>
            <date month="October" year="2023"/>
            <abstract>
              <t>This document specifies an RSA-based blind signature protocol. RSA blind signatures were first introduced by Chaum for untraceable payments. A signature that is output from this protocol can be verified as an RSA-PSS signature.</t>
              <t>This document is a product of the Crypto Forum Research Group (CFRG) in the IRTF.</t>
            </abstract>
          </front>
          <seriesInfo name="RFC" value="9474"/>
          <seriesInfo name="DOI" value="10.17487/RFC9474"/>
        </reference>
        <reference anchor="OPRFS">
          <front>
            <title>Oblivious Pseudorandom Functions (OPRFs) Using Prime-Order Groups</title>
            <author fullname="A. Davidson" initials="A." surname="Davidson"/>
            <author fullname="A. Faz-Hernandez" initials="A." surname="Faz-Hernandez"/>
            <author fullname="N. Sullivan" initials="N." surname="Sullivan"/>
            <author fullname="C. A. Wood" initials="C. A." surname="Wood"/>
            <date month="December" year="2023"/>
            <abstract>
              <t>An Oblivious Pseudorandom Function (OPRF) is a two-party protocol between a client and a server for computing the output of a Pseudorandom Function (PRF). The server provides the PRF private key, and the client provides the PRF input. At the end of the protocol, the client learns the PRF output without learning anything about the PRF private key, and the server learns neither the PRF input nor output. An OPRF can also satisfy a notion of 'verifiability', called a VOPRF. A VOPRF ensures clients can verify that the server used a specific private key during the execution of the protocol. A VOPRF can also be partially oblivious, called a POPRF. A POPRF allows clients and servers to provide public input to the PRF computation. This document specifies an OPRF, VOPRF, and POPRF instantiated within standard prime-order groups, including elliptic curves. This document is a product of the Crypto Forum Research Group (CFRG) in the IRTF.</t>
            </abstract>
          </front>
          <seriesInfo name="RFC" value="9497"/>
          <seriesInfo name="DOI" value="10.17487/RFC9497"/>
        </reference>
      </references>
    </references>
    <?line 1918?>



  </back>
  <!-- ##markdown-source:
H4sIAAAAAAAAA+196XvbRpL3d/wVGPnDUAlJA+CtGc+7iu3Yembi+LHsbHa9
WRFHQ8KaJDgEaUnxev/291fVBxoHddhOMrOb7GwiAn1UV9ddhe5er+dss+1C
HLkHx6t8db3Md4X7KtyK3t+yZbYVift4IxKx2mbhosDf1+ttfr4J1xfXB04Y
RRvxnnq+elx7FWOE83xzfeRmqzR3nCSPV+ESsySbMN32MrFNe+tN9j6Mr9dh
UfTCTdyLeYSe5znFLlpmRZHlq+31Gp1Onr7+1lntlpHYHDkJhj5yMO3AeS9W
O/ztuuebfLc+cl++Ovnh+PG/vTw+PcVD2felnMV9iWnwcBlmiyNXTd2juf+F
gOnnm3O8BRgXR+7Fdrsujh4+pLb0JHsv+rrRQ3rwMNrkl4V4aA9DUGTbi12E
JdPqLs/tBT6U60ZntFtgBcW2nKelfV+O1c/ysufDW3HXv9guF44T7rYXOTDl
9jCZix0ojtzHffffdiv+LTficbi9yIR5iKWFq+zncAukH7nH6/VCdN2TVdzn
t0KiLeY+17vVv5zTg36cL+uTHPfdf83zxJ7oYpMV23x9ITaVt5jxhoku/+VC
hOtsdR5l26K/EtvKRBjn2/Dn3nOxWYWrRPxsTXe8WeJR3vKeJ3y8yHdJim0V
9nzhZpmGP1/8S2ze8tqcVY7Rttj/I8chQja/XPevoLQjHkPxz1/FtUh6P4hN
lmYx49EtOcpmonSTL93jxbmINmEWu98dPy7kOOHmXFhkIbDJq20/C+MNU17g
+YOHI3+Mxq+e/nByevL65MWzswYcr8T7rMi2QN2dQbrj9MHwoT8aBWj9zTen
lTlPQW9b9xlxoXuana/C7W4j7jqsh2EnQx71yWt/Uhn4ZLne5O8hhar4crGr
4Oww3mJdi89b5yJbvesXBNW52NC2P4wvwvVWbB76Xt/3vMnD2WTaG/QG/qw3
ng1Hg97oLPAw1IuT09ePd5v3AgT55PsTtO2PvWD6kJ73vz15edr3p+PeCC1P
nz72K8s6wBPXP3KfLhbZGmtweZyaDG0D9vLysl+I+JxRhz/83vugv05SbsyS
0U2xUEncRg7wPz31X8VCp1vgMNwkoMd84z5NgbkMaKrAoHa0A2ifHTpOr9dz
w6jYEt4d5/VFVriQ67sldSvWIgb2ReFuL4R7qypxO9AZh5DD+TaP80XXCeUI
4ULJIDdP3Xe8re/tbQ3NwLHFUZcQlm6xW6+JCrEcZ4NZ3QXNil2FUIJ+stvH
4cqNBGYXBR4BMGbJeMEI2OZuIbAbGxdL3+ZOkS+Fm2ZXaCZVEIG2zZai6LqX
EGrCFWF8oQeTYGYEn8Ej0eji2o3y3SpxMLyaCNu3EVtJyyHkoFpjT6Eydte7
CA9dI3jyVRerxFzbi3DLszr1WXcrIucwWgi5JtqMHP/aYBrgSSwW9F9+usnO
sxV2okSMgz95JIkxGq4Q6W4BCGwAC7XsUONpJQTICAvbXmA/QeCu2oIeb4Eb
xrEolNyzNpCxUPQlXS2zJFkIx3kAXbDd5Mkupqn+majMcRTSWDdvBXZcDsq7
0WlOc9h1r8XWTbI0pf1h9BQYDeoHyHwnVr0oLETiaPAL2gez9RZoTXpe7hbb
DJpV0ilDnu+27vssh+0BYB1NJ9ki214TFiQUhGObpIq+e8p7jLmLYicq+DCU
XDBUDrTmPqp326meWatJ5X3nOIX8vSTp1DVz0CIVbJgvs/dG0p6o8K1b8q1z
Z74lZZMlgvYwp9aEbBDwJXgmwc5BETmSit33oKHE7aA3kLorsFBGUHJog0Vr
NOvfu9inbeJjHWaby6wQFkd33aWAjQa9bq0VWFnlW5faYLprd3uZV4RCYXAD
60jvQQli14WJw6i1xpSDVUBy1Ch4FouEYLCWSSsPVzGwvcgv++6LfAtwr7vc
YRleZcvdshSdNegUOiujKWkbXfMIluQpeawUTIXYEkUboWTLqY34+y5jSVWX
3z+LTd57t8ovFyI5Fw5vN2gyIi6pESoTAWEINJ8Tm91A49YeO/YeuyzG4nCt
OW5BIxeKtbCYCmPVxS1DQELW0QI2gYR1SwmL37fJ2Afu43z1nmagEYkwn4g0
W2X8G68f8LYp6Ze4r8Vmma3yRX5+TTIYW5svsLm08eluFZejrEwv4BkLSVgJ
7M5Z3tD2adlNct6N8xUW1bnyum6/3++6Vy8OjwgwchdXRiJH15CrMDNoW2Ho
Y6niKlySowATRg3hXXl+18W/A2/A/x16I2986D6iv316Kp/0MSuNV5xt8zMY
ngw0/ks/+fmRRAxEemhPTLtJTRV9AqO9lThn65+6C7IVAc2NQzNOMgJ8KcUy
xMj3p8HJS255Enx/+tJlyVLEmywS1BlDfvjwh1ffPp56/uTjxy5ImDQWTbu4
ZtYSkhyAWlBNuRf5WrA6AgZt9Dlk7blRdt4TqyQL1ct8kxD4PZAQXKXlmVrQ
2W6VEc12vuu6tDHPxEoOGqqGXVe1AM0nGU0R7bYMN/d3XxFGxPZSiJX7HZ7G
i11BCKPlvsAmqt9dN+sL7P537p8fua/cP7sv9sNyxr1I4pyB0xmyrnt6C3AA
oxW8TwOOqK40u16BorG1JHTFFSaRSplk55ZoV81VuKckrMjcWpzD1NpeLCW/
QNJAgJJ3VNl3qdGMnUOEswihYMBFDokD9+U1VPgK7P4OAqYQOzTNE9F3WXlo
MoC78I7IrgRmvVOqdh1uoACgVQtHgQETy26LmdZkIoSLnQDXvTTtgbSYxBoY
r4DPoJuA0mKiRWHsE7zYZKHiA0JJnJHTX+wy2qWNUsZ9KU+M3Ji/dt+8ffHT
HJTOZhDzHZpvwmuXpCvQ84bm3oYZ674XUMgCyCT1fL0WzmsSroL/duf5Ovz7
TsxZURKE2Bb0gR0kyT4lEsEGbaCDiDDgMIV990SN57C24YkJWlYR2SoRpDlo
SQzgRrLx/M3b/wLMJVl4RC7/JWm5ur4Prz9WFscIJ2SCeGhBgEpICWEWBaTM
+w4JPlLpefRfkADu/GoOw0W4lxvC53whVp2rwzkNCcVBciHDAHh6Dis1k4xO
gv9UijO1aQww4D8ojeRXUJKi2B50qw+LNUAU9PSlpbDxm1Bx8Do8P2CD7/j0
8cmJlplQbeDJRdGv6ww8XhZ7VIRF96wjHrPOosCdIivWUuE23/TJtA9ZsNoG
w0YuQEKG8dk5cOJ8s+FFsLWiLJuNWhZhbQleqQwkCUmbSOHK4RbGlK7aLWwd
VoxQkmHSWCaVsgQzMaD2lO1gs1+BzXMUkMp/IMcEjPg3/LmpWXzSsZDmkYT3
j0UVPkcPWh9NhJsVS68LQos0eCrDSLPHDbdKfBZdFhgVO7NsrRbx0KxSChfL
li22Yk0E8ec/wMt7/f2T7ztxeAn5nZD5IZQeY/o4AgK34Tn+bSYntSBngJLe
iqstHtijtz9lR7D2DE7mX8gKAjGTybSCqBKFJFMWbBvpcDZF8UUoBQnwuAw3
0HtiDVUqVvH1EVHrnEMk8yP3mFuIHutWGZAudb822o9fnlhiPxJgD5rxw4d1
fv7xIxkUp0LgpyU5i48fOUJjogIZi2GS3aBBnoftO1oazf89zy8DNx8e0LiO
c1JbFMuREFb7UsjNtzGAUUkAJ0m2ZXVoLcuRy1JrZrBgdpDOZWKCtoO9eyHI
6KWwnLRJpBt5smrih0nrWks/t8OBChKnUqNmzCnb60Mm+XOt701Ioyo2wfI8
aN99A29kQf4HbZse3HgVRKCS9hIdCpGrUhNAzEh8CQ6CKPeCnZmNVGNwsdRY
pksJSsc8e9Yt3z8/7DunJHrluqUkXGWgbLKkSM0uGSDl8sQXeRYLtSyJN3cO
hLOmkcBLISeRqdqp5XdeM6TLEBi4yC8FZEVXmSqlk78RVXFhD8H7GmrNiXn7
TYpcYLiipEm1rEs4RBewVxJyXaB0sU3S1lT6056zf6iUBFynkHAHgpHTG7Kh
TZN0SHr067mSlIosNM6d+clcegkWMUFDH8951vk38wqCunjjfu1+A0/hG/wX
rTKYROTahTAbTJygxOcx3nUlTjA+DassPjWcJAxGMJlaJVi9Y9tA4Fk7vWNy
Ufg/mBt/EuynYJZwozWNDkKBFuYbho68WOhvFZykqYFKUoSVuGEFbAuJ7NE5
mg+OjboRixQT9Py5DoqwiCApweaENHM2XxGQBDr8NUZXaZfoIYESIHX91fEj
WgxL1BAyHf64soxbl4d1KdlB3uyqjRHrwEg8fYdxYPx3NoeA5RlE1Io50mz8
MW/7NzL8ADeA1Bms6fw8ZDOcTN8IbWnwLTHyKoEUMj7CO2vLiEbefXVMa9oB
P++4S/ugsB6/kZhVDpveKQrhucfSJlSWtsQHTAdjoTAk82ffdtaHQGWoOD7N
xCJh05xZaC0NHMpcGU4qZVjd/dAijqjoA9xtX3nc657/cd6vhVWl0QTDs3Dm
TyUeFfNIlNtWZk3oqj1j15dDGgSGo5ZYc18d51/JML4kiRdmC3KrTUBGsV7p
0JqwS0QO9/v8nSST0GmoEbYaWe11YFd8z36MjnHXxaPbIe+OhNoh2WsnSpbU
O9ZlTOsYJ3KMZ5pc64PUdURFrKDj87C4eJ2zLu1cdTkmiBGeKG1A3oGMLi3D
NfnxxitSEZKCPAJJc/B+Qw6FkZfH3MXmvjunMefKpbKWohS4ZFYMD96EdhCr
YreR4QVyebXECxMKUpHpQ7G/7D2p+vkrsEYb/CDfjJQtB2rIBmZweBmkfLIY
zEvwbEgtFUKFCNdrDqCT9RPvNoR3MCmkwzYTpaVtnFxSnsaIkgGODx/+cNJ7
0s8227QXp5vzHqy2i94278WUVyPLSqNbEvSviW80ZM7+tPXdvroujcodfa8/
kmt9xRERtVYKsV3kOfE4WEoGS1RIi3zcFkB7SiWdrHiXOsUhpZW3u81KsYd8
bkIMpZyYF3PiUiXLlEvE+RahBEvnGIN9F65ZV5bSBkqESjbYR6L4JRuQ7LXL
TZhHu5QVueQq5efOXwie5AmkYW0atMdEx1s49+ttIR2+tY7x2WNuc7nZFVik
rE0hpaASU7VoWmkmQz/0QKYAqtBiqKqZo8zpNjmiosJm30nYbUKK+YcYxl7S
ZkPKMCWqUM/k2BI+vGOrZK/oIsTqSf8Eqt3jYfD7nuVnqHhQllQ8OWtLFYEV
ZkerhFDdTRUtvWU/i/p+qjnusZ01MIjv9iL67mhmxHF49tNRx6YTZ53abEey
YhIOCsF8MGqjsNyIZ0SVTulVVF49cp/1LV3EBFw25de2xH7Wb3BmOdhh1z0o
+x4cSvtFar+GpjaTPlOzmgfP6QEWWFB5yAXBTsjasCQhNVJdmGsvrGY4NLCu
IoKFsiVkFQPTv/a5LaOY7FoK+6VhLBgEO9tZd0qVg00hJzI53rOjqco0WODK
dAo5hzpEJT0ZemJioeS60I72oCKkvJfPN7uViUKrUBNnQjm241TDgtuLjRAl
Sawh9kVx5Dh+nypeNLo4v6Q9fG7TtcNF2m8uKtEgDtPIPJXrcmyIGCdS8TmN
yw2VR/FOybmVxOIf0jivaCkYf7s1KaCgbyfhdXqwFcoGDuROhdKC5pwqQWg3
rSR+OUO7t9qimXfmMrMbV6HBpYUM+q4d/bxpBTrwWEtLc0kFCZoDWytwcU8l
aa1z0nYathqrW+bSOFtRS45/YJeULqLh6hlkqZ44dld1NGSSsp64tpPCNJyu
GWAoaH527iuJ2m1t5L3k4bjKSrNhJPxKP8QwRyTIL6I2y2jBGWU2pXQ2k4si
TOuia6LZgBQzRrsMxmW+cr5ZwKlzX50eY8b/983fTl486eHHo1ffPp4NJ0MI
D0LK9yB+xoD7ktMpyi761og19P3+5atvT2W/2eTjRxlUpagSliVDXJo5uWqS
UvUUU64CXHTZlGwpKVHujcM8VyvZKEnHKumphqA7ss6hdDUq8U5ODB+qLZH5
EGYWE9gxWXvyrpRTTgAlVOfQHvZ2yqa6iCJUdTHNRbdURZUTqcx84YgrsDqI
kLDaAlHfPVZBdMDTrWTJSbFI4UbsvsipfM9dcI2GDCEyHH0uMTWpdAjYNDvf
KUaztqICqCpbSlXk3alWJyh+KeMETRkWnpPkVlENOVqy4xRJJQTm6OIaWosK
oWdcu0V2ArljMnCMSeUgmU48xgIQJX2VLFFbKx/LNBYBkPR263zVtrQIG/eO
gasWAp3v4NBA/soKGCqFoQiCI6OQku1Ubgdjbo1yLZQDohleCkKpvqTm0Iav
rSNl1Jo02TOjydwPD6QWcXQQ9l1F032KgiPt1jWBJInyl7IZTU4t1UNuj2dd
Eioye0XK/n/+538ADUy6I/cF6NSRTv6RyfeUg1GtZc+98o6UAyV/+tWfQfWn
x+IKG20e9+rwyGF/9KhgVAYX+bdf+x2Uv50yf0shCjbcnjlOIlKMDfzKCeAa
oueVx8Zh1Wek5/6e58Ge52Yl7e9/pHkw2Ve2oUo249d218rb57IjAQJo2l4R
LACo5ZU0MxtbxMUnVz7+P+ha8x5260jv/IiGP6Lhj8Ehk4CK4ckcnazjYsNJ
BamMH5FY1HMkqUdmV9wPAGsH+2YKXLx9IX76k/vwodtiiP/oHZYt/Rtb+lbL
4MaWWMXH+hr/VC5MeWCapcWK6guSMn0pl0CSE2/hp1UHwi4MvmJnnHj6RAvy
Dw+MNSVnWe8267wwqQ0j8Y3NoE3PpkwljZirsDZ5BSrAVM3hOncwCFvSnUUp
SZOcRWzMEROVnlSN/kSCUBYHqLlNxqYmHWvrMokc7QQo7VfWT+YuFaPZYq2k
MBkQzjdZ16rhdTg8FWXbTbghedVWP6xSp+wriastVTqUU69ylUVjZ5XfKe+j
ML6HY3wPdmCl62ElyUvRK/eJzMC2HHfV1NXVcM1mxqkLpUFrajClMyIbGdO2
Ldxe31eVeKrh4ZQ9ecvW79H6fOXMa49SJ8dJweF1Xy9ebZBy75tWv5oOKE1l
o640lU1JQKWtYi9eTIkjuX47c6+UrmzN1UsJD1bDUtvYN2GMJlILKudRD+SG
bu2JK2juq2x1BYvBXizyVDYiFRWlVIYOSdVAZHQty6WKolb0qsGxUWz2yb2m
vImiobqrqCpdV8bVVbm8ahVwG9O2LnYgF0sz7aUaWd8rR2M5KlQCuFb/ZdfL
FLtI2VQyt//A9qdVwY4lXBX5OiodFtYpvqtXImtEqeKXK/0pad/Gh1nRVGEd
uVunkq+6ui2lM2VVTqOiqFMt3Th0pBHFdR7VV0fuk3AbdjW4pRJogOYo48se
RFpHS//pKjYGUBdUteGcBisCMtcZ8L5sG1Tb4h1c2D2N1Swvic+O3H//K//R
NZyH/7EXXMbsVNiJAWL5w9NRWLOCQg22Nvs0yFVAy7cKyApoVudIG1ApHCrg
j3DYHHAT3NyjNollRhLCnx1JUxJ/lqE7C4/VRHXVyrP7PL9LH7Li2GC9I3kR
Ppd7TNZlYMU/1fNq9657UH1wwDbknvE2e0xgueePCI6v7Ojm1zSS9eQ5w6Ta
Bo22Qb2tTYPo8l34rokPftlZwmBdwrLd4L8b/Jch6srJDsuRqAjBftWtTEEN
K7Qqm9sjWxb2PskgbUspNdWzHvOM0hAc0NJiU1cAVFioXQZIxiv9UIpUgIqN
zLvBHO/vMccZFWw9l4+CxqP4gupRV+fi7YvCeqy1kdf+2G9/HLQ/HsjHH93G
/t7FVpcYqJnq5ZYHMNFBX6OvOM3S1CtKydcUS2AplpbdsEx0ZbPKGsDSeKmo
H+cm9VMaLPU9s4qeHdOuTfXIV52i5vZ13aLu4xs6tfRSvVvdoacvfvjdYdcs
ulyp6/VrHv8t7f1+LSRwS/ugvydmYPczcl0mpQnfV16/XNzeqEK3xfdQK7Ji
DW2t1DqsCERbq6D/z6e0bXvjjTW7/uqABUxZ41FmTiQddt159NUzzmkC0Ddc
omkrPwO8fEXtfvSOd1f2TLurbJFR/cU6p69MaIofeT9/9O/Q0ueWwR1a8vY8
v71hqZnkfsqV3hfV2KY33RIpLfRSqIKnKgjFP4BV8vQqFmu2zml+/u78mrPH
XZlXTmTxm5FTlY9LOZu8z7LZI72KG8QXcVKWKij2WAWlsf5Ifh8uPwqXSXAL
foeS9O2mzRs8jiqWCh6a7VMvOzUo+z967tfmu/PGqvoc1NP+suS9G5sHdnNt
0jDDKABa+tihxYpRxexT7VfC7XNw8YYGdAzC8/K9NTJbRhZTtBhs1tumpnKs
xTe2G+K9W2McXn9XLqcrge4yaLaNdscu3SroVuDTNrQUWd9oabna8JONP9fy
U1Nq06/VintTNdfMequPee21R37zUdB49Lzx5Fc0COuPh+2PR+2Px21WpXz1
yRFgywobS7tyatmV36qgjm1g1uzKwe12ZalG92mHMhrl6JCf+rZNCeEbIx2N
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
1GmDG53S7PXopMH4neP8f+EkGmqTNQEA

-->

</rfc>
