<?xml version='1.0' encoding='utf-8'?>
<!DOCTYPE rfc [
  <!ENTITY nbsp    "&#160;">
  <!ENTITY zwsp   "&#8203;">
  <!ENTITY nbhy   "&#8209;">
  <!ENTITY wj     "&#8288;">
]>
<?xml-stylesheet type="text/xsl" href="rfc2629.xslt" ?>
<!-- generated by https://github.com/cabo/kramdown-rfc version 1.7.29 (Ruby 3.4.4) -->
<rfc xmlns:xi="http://www.w3.org/2001/XInclude" ipr="trust200902" docName="draft-yun-cfrg-athm-00" category="info" consensus="true" submissionType="IRTF" tocInclude="true" sortRefs="true" symRefs="true" version="3">
  <!-- xml2rfc v2v3 conversion 3.31.0 -->
  <front>
    <title abbrev="ATHM">Anonymous Tokens with Hidden Metadata</title>
    <seriesInfo name="Internet-Draft" value="draft-yun-cfrg-athm-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="M." surname="Raykova" fullname="Mariana Raykova">
      <organization>Google</organization>
      <address>
        <email>marianar@google.com</email>
      </address>
    </author>
    <author initials="S." surname="Schlesinger" fullname="Samuel Schlesinger">
      <organization>Google</organization>
      <address>
        <email>sgschlesinger@gmail.com</email>
      </address>
    </author>
    <date year="2025" month="October" day="20"/>
    <area>IRTF</area>
    <workgroup>Crypto Forum</workgroup>
    <keyword>anonymous credentials</keyword>
    <keyword>hidden metadata</keyword>
    <keyword>fraud prevention</keyword>
    <keyword>zero-knowledge proofs</keyword>
    <abstract>
      <?line 63?>

<t>This document specifies the Anonymous Tokens with Hidden Metadata (ATHM) protocol, a protocol
for constructing Privacy Pass like tokens with hidden metadata embedded within it unknown
to the client.</t>
    </abstract>
    <note removeInRFC="true">
      <name>About This Document</name>
      <t>
        The latest revision of this draft can be found at <eref target="https://cathieyun.github.io/draft-athm/draft-yun-cfrg-athm.html"/>.
        Status information for this document may be found at <eref target="https://datatracker.ietf.org/doc/draft-yun-cfrg-athm/"/>.
      </t>
      <t>
        Discussion of this document takes place on the
        Crypto Forum Research Group mailing list (<eref target="mailto:cfrg@ietf.org"/>),
        which is archived at <eref target="https://mailarchive.ietf.org/arch/browse/cfrg"/>.
        Subscribe at <eref target="https://www.ietf.org/mailman/listinfo/cfrg/"/>.
      </t>
      <t>Source for this draft and an issue tracker can be found at
        <eref target="https://github.com/cathieyun/draft-athm"/>.</t>
    </note>
  </front>
  <middle>
    <?line 69?>

<section anchor="introduction">
      <name>Introduction</name>
      <t>This document presents a construction for Anonymous Tokens with Hidden Metadata (ATHM).
ATHM is a cryptographic primitive that enables an issuer to issue an authentication
token to a client in a way that the token carries no information about the client
identity except a hidden metadata value the issuer sets during issuance. The value
of the hidden metadata comes from a fixed domain and the client can verify this property
of the hidden metadata of the token it receives. The metadata value remains hidden with
respect to any party which does not have the secret key for the ATHM construction,
including the client. Only the issuer who has the secret for the construction can verify
the token and read the hidden metadata value.</t>
      <t>Similarly to public metadata proposed in the context of existing anonymous tokens schemes,
the hidden metadata enables the issuer to partition clients into a number of groups proportional
to the domain of the hidden metadata. During token redemption the issuer can identify which group
the client belongs to based on the metadata value. The difference from public metadata is that
this value of hidden metadata is only readable by the issuer who has the ATHM secret key,
while public metadata embedded in the token is visible to everyone.</t>
      <t>Anonymous tokens are used for client authentication and their meaning is to convey trust
in the client. In most cases the trust assigned to a client is determined based on the outcome
of fraud and spam detection processes. It is often crucial that the signals used to obtain
the verdict and the verdict itself remain hidden from the adversary, who can adapt its behavior
to avoid detection if it knows when and why its behavior has been identified as fraudulent.
ATHM can provide the means to issue authentication tokens that provide limited hidden fraud
signals while preserving strong anonymity guarantees.</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>For any object <tt>x</tt>, we write <tt>len(x)</tt> to denote its length in bytes.</t>
          </li>
          <li>
            <t>For two byte arrays <tt>x</tt> and <tt>y</tt>, write <tt>x || y</tt> to denote their
concatenation.</t>
          </li>
          <li>
            <t>I2OSP(x, xLen): Converts a non-negative integer <tt>x</tt> into a byte array
of specified length <tt>xLen</tt> as described in <xref target="RFC8017"/>. Note that
this function returns a byte array in big-endian byte order.</t>
          </li>
          <li>
            <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.</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>String values such as "CredentialRequest", "CredentialResponse", and "Presentation" are ASCII string literals.</t>
        <t>The <tt>PrivateInput</tt> data type refers to inputs that are known only to the client
in the protocol, whereas the <tt>PublicInput</tt> data type refers to inputs that are
known to both client and server in the protocol.</t>
        <t>The following terms are used throughout this document.</t>
        <ul spacing="normal">
          <li>
            <t>Client: Protocol initiator. Creates an encrypted request, and uses the
corresponding server encrypted issuance to make a presentation.</t>
          </li>
          <li>
            <t>Server: Computes an encrypted issuance for an encrypted 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 issuance and presentation steps.</t>
          </li>
        </ul>
      </section>
    </section>
    <section anchor="preliminaries">
      <name>Preliminaries</name>
      <t>The construction in this document has two primary dependencies:</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>
        <li>
          <t><tt>Hash</tt>: A cryptographic hash function whose output length is <tt>Nh</tt> bytes.</t>
        </li>
      </ul>
      <t><xref target="ciphersuites"/> specifies ciphersuites as combinations of <tt>Group</tt> and <tt>Hash</tt>.</t>
      <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 KVAC 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>Generators(): Outputs the generator elements of the group, (generatorG, generatorH)
generatorG = Group.generator
generatorH = HashToGroup(SerializeElement(generatorG), "generatorH"). The
group member functions GeneratorG() and GeneratorH() are shorthand for
returning generatorG and generatorH, respectively.</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><xref target="ciphersuites"/> contains details for the implementation of this interface
for different prime-order groups instantiated over elliptic curves. In
particular, for some choices of elliptic curves, e.g., those detailed in
<xref target="RFC7748"/>, which require accounting for cofactors, <xref target="ciphersuites"/>
describes required steps necessary to ensure the resulting group is of
prime order.</t>
      </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 instantiations of the following functionalities:</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>
        <li>
          <t><tt>Hash</tt>: A cryptographic hash function whose output length is Nh bytes long.</t>
        </li>
      </ul>
      <t>This section includes an initial set of ciphersuites with supported groups
and hash functions. It also includes implementation details for each ciphersuite,
focusing on input validation.</t>
      <t>For each ciphersuite, <tt>contextString</tt> is that which is computed in the Setup functions.
Applications should take caution in using ciphersuites targeting P-256 and ristretto255.
<!-- See {{cryptanalysis}} for related discussion. -->
      </t>
      <section anchor="athmp-256">
        <name>ATHM(P-256)</name>
        <t>This ciphersuite uses P-256 <xref target="NISTCurves"/> for the Group.
The value of the ciphersuite identifier is "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 [0, 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, return the
result. If it fails, 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="anonymous-token-with-hidden-metadata-protocol">
      <name>Anonymous Token with Hidden Metadata Protocol</name>
      <t>TODO(caw): writeme</t>
      <section anchor="setup">
        <name>Key Generation and Context Setup</name>
        <t>In the offline phase, the server generates a public and private key using the KeyGen routine below.
The type PublicKeyProof and the function CreatePublicKeyProof are specified in <xref target="public-key-proof"/>.
Further, they must agree on a string <tt>deploymentId</tt> and a number of buckets <tt>nBuckets</tt>. The <tt>contextString</tt>
as used in <tt>HashToGroup</tt> and <tt>HashToCurve</tt> is <tt>ATHMV1-&lt;group-name&gt;-&lt;nBuckets&gt;-&lt;deploymentId&gt;</tt>. An
example of <tt>contextString</tt> is <tt>ATHMV1-P256-4-example_deployment_id</tt>.</t>
        <artwork><![CDATA[
Input: None
Output:
- privateKey:
  - x: Scalar
  - y: Scalar
  - z: Scalar
  - r_x: Scalar
  - r_y: Scalar
- publicKey:
  - Z: Element
  - C_x: Element
  - C_y: Element
  - pi: PublicKeyProof

Parameters
- Group G

def KeyGen():
  x = G.RandomScalar()
  y = G.RandomNonzeroScalar()
  z = G.RandomNonzeroScalar()
  r_x = G.RandomScalar()
  r_y = G.RandomScalar()

  Z = z * G.GeneratorG()
  C_x = (x * G.GeneratorG()) + (r_x * G.GeneratorH())
  C_y = (y * G.GeneratorG()) + (r_y * G.GeneratorH())
  pi = CreatePublicKeyProof(z, Z)

  return privateKey(x, y, z, r_x, r_y), publicKey(Z, C_x, C_y, pi)
]]></artwork>
        <t>The output <tt>publicKey</tt> from this function is wire-encoded into <tt>publicKey = 3*Ne+2*Ns</tt> bytes as follows:</t>
        <artwork><![CDATA[
struct {
  uint8 Z_enc[Ne];
  uint8 C_x_enc[Ne];
  uint8 C_y_enc[Ne];
  uint8 pi_enc[Nproof];
}
]]></artwork>
        <t>The <tt>Z_enc</tt>, <tt>C_x_enc</tt>, and <tt>C_y_enc</tt> fields are the serialized representations
of <tt>publicKey.Z</tt>, <tt>publicKey.C_x</tt>, and <tt>publicKey.C_y</tt>, respectively.
The <tt>pi_enc</tt> field is the serialized PublicKeyProof, as defined below.</t>
        <section anchor="public-key-proof">
          <name>Public Key Proof</name>
          <t>The server public key carries with it a zero-knowledge proof of knowledge
of the corresponding private key. Clients verify this proof before using
the public key for token issuance. The procedures for creating and verifying
this proof, CreatePublicKeyProof and VerifyPublicKeyProof, are detailed below.</t>
          <artwork><![CDATA[
Input:
- z: Scalar
- Z: Element

Output:
- pi: PublicKeyProof
  - e: Scalar
  - a_z: Scalar

Parameters
- Group G

def CreatePublicKeyProof(z, Z):
  rho_z = G.RandomScalar()
  gamma_z = rho_z * G.GeneratorG()

  ser_genG = G.SerializeElement(G.GeneratorG())
  ser_Z = G.SerializeElement(Z)
  ser_gamma_z = G.SerializeElement(gamma_big_z)

  challenge_transcript =
    I2OSP(len(ser_genG), 2) + ser_genG +
    I2OSP(len(ser_Z), 2) + ser_Z +
    I2OSP(len(ser_gamma_z), 2) + ser_gamma_z

  e = G.HashToScalar(challenge_transcript, "KeyCommitments")
  a_z = rho_z - (e * z)

  return PublicKeyProof(e, a_z)
]]></artwork>
          <t>The output of CreatePublicKeyProof is a value of type PublicKeyProof,
which is wire-encoded as the concatenation of the <tt>e</tt> and <tt>a_z</tt> values,
both serialized using SerializeScalar, yielding the following format:</t>
          <artwork><![CDATA[
struct {
  uint8 e_enc[Ns];
  uint8 a_z_enc[Ns];
} PublicKeyProof;
]]></artwork>
          <t>The VerifyPublicKeyProof routine, which takes as input a server public key, is below.</t>
          <artwork><![CDATA[
Input:
- publicKey:
  - Z: Element
  - C_x: Element
  - C_y: Element
  - pi: PublicKeyProof

Output:
- verifiedPublicKey if valid, False otherwise

def VerifyPublicKeyProof(publicKey):
  pi = publicKey.pi
  gamma_z = (pi.e * publicKey.Z) +
    (pi.a_z * G.GeneratorG())

  ser_genG = G.SerializeElement(G.GeneratorG())
  ser_Z = G.SerializeElement(Z)
  ser_gamma_z = G.SerializeElement(gamma_big_z)

  challenge_transcript =
    I2OSP(len(ser_genG), 2) + ser_genG +
    I2OSP(len(ser_Z), 2) + ser_Z +
    I2OSP(len(ser_gamma_z), 2) + ser_gamma_z

  e_verify = G.HashToScalar(challenge_transcript, "KeyCommitments")
  if e_verify == e:
    return verifiedPublicKey(Z, C_x, C_y)
  return False
]]></artwork>
          <t>The output <tt>verifiedPublicKey</tt> from this function is wire-encoded into <tt>verifiedPublicKey = 3*Ne</tt> bytes as follows:</t>
          <artwork><![CDATA[
struct {
  uint8 Z_enc[Ne];
  uint8 C_x_enc[Ne];
  uint8 C_y_enc[Ne];
}
]]></artwork>
        </section>
      </section>
      <section anchor="athm-protocol">
        <name>ATHM Protocol</name>
        <t>The ATHM Protocol is a two-party protocol between a client and server where
they interact to compute</t>
        <artwork><![CDATA[
token = F(privateKey, publicKey, hiddenMetadata)
]]></artwork>
        <t>where privateKey and publicKey contains the server's private and public keys, respectively
(and generated as described in <xref target="setup"/>), and hiddenMetadata is an application-specific value
known only to the server.</t>
        <t>The protocol begins with the client making a token request using the verified server public key.</t>
        <artwork><![CDATA[
verifiedPublicKey = VerifyPublicKeyProof(publicKey, pi)
(context, request) = TokenRequest(verifiedPublicKey)
]]></artwork>
        <t>The client then sends <tt>request</tt> to the server. If the request is well-formed, the server computes a token response with the server private keys and hidden metadata. The response includes a proof that the token response is valid with respect to the server keys, and a maximum number of buckets for the hidden metadata.</t>
        <artwork><![CDATA[
response = CredentialResponse(privateKey, publicKey, request, hiddenMetadata, nBuckets)
]]></artwork>
        <t>The server sends the response to the client. The client processes the response by verifying the response proof. If the proof verifies correctly, the client computes a token from its context and the server response:</t>
        <artwork><![CDATA[
token = FinalizeToken(context, verifiedPublicKey, request, response, nBuckets)
]]></artwork>
        <t>When the client presents the token to the server for redemption, the server verifies it using its private keys, as follows:</t>
        <artwork><![CDATA[
hiddenMetadata = VerifyToken(privateKey, token, nBuckets)
]]></artwork>
        <t>This process fails if the token is invalid, and otherwise returns the hidden metadata associated with the token during issuance.</t>
        <t>Shown graphically, the protocol runs as follows:</t>
        <artwork><![CDATA[
   Client(publicKey)                      Server(privateKey, publicKey, hiddenMetadata)
         ---                                                  ---
  verifiedPublicKey = VerifyPublicKeyProof(publicKey, pi)
  (context, request) = TokenRequest(verifiedPublicKey)

                                request
                            --------------->

                         response = TokenResponse(privateKey, publicKey, request, hiddenMetadata, nBuckets)

                                response
                            <---------------

  token = FinalizeToken(context, verifiedPublicKey, request, response, nBuckets)

     ....

                                  token
                            ------------------->

                    hiddenMetadata = VerifyToken(privateKey, token, nBuckets)
]]></artwork>
        <t>In the remainder of this section, we specify the TokenRequest, TokenResponse,
FinalizeToken, and VerifyToken functions that are used in this protocol.</t>
      </section>
      <section anchor="tokenrequest">
        <name>TokenRequest</name>
        <t>The TokenRequest function is defined below.</t>
        <artwork><![CDATA[
Inputs:
- verifiedPublicKey:
  - Z: Element
  - C_x: Element
  - C_y: Element

Outputs:
- context:
  - r: Scalar
  - tc: Scalar
- request:
  - T: Element

Parameters:
- G: Group

def TokenRequest(client):
  r = G.RandomScalar()
  tc = G.RandomScalar()
  T = (r * G.GeneratorG()) + (tc * verifiedPublicKey.Z)
  return context(r, tc), request(T)
]]></artwork>
        <t>The output <tt>request</tt> from this function is wire-encoded into <tt>Nrequest=Ne</tt> bytes as follows:</t>
        <artwork><![CDATA[
struct {
  uint8 T_enc[Ne];
}
]]></artwork>
        <t>The <tt>T_enc</tt> field is the serialized representation of <tt>request.T</tt>.</t>
      </section>
      <section anchor="tokenresponse">
        <name>TokenResponse</name>
        <t>The TokenResponse function is defined below.</t>
        <sourcecode type="pseudocode"><![CDATA[
Inputs:
- privateKey:
  - x: Scalar
  - y: Scalar
  - z: Scalar
  - r_x: Scalar
  - r_y: Scalar
- publicKey:
  - Z: Element
  - C_x: Element
  - C_y: Element
  - pi: PublicKeyProof
- request:
  - T: Element
- hiddenMetadata: Integer
- nBuckets: Integer

Outputs:
- response:
  - U: Element
  - V: Element
  - ts: Scalar
  - pi: IssuanceProof

Parameters:
- G: Group

def TokenResponse(privateKey, publicKey, request, hiddenMetadata, nBuckets):
  ts = G.RandomScalar()
  d = G.RandomNonzeroScalar()

  U = d * G.GeneratorG()
  X = privateKey.x * GeneratorG()
  Y = privateKey.y * GeneratorG()
  V = d * (X + (hiddenMetadata * Y) + (ts * publicKey.Z) + request.T)

  pi = CreateIssuanceProof(privateKey, publicKey, hiddenMetadata, nBuckets, d, U, V, ts, T)

  return response(U, V, ts, pi)
]]></sourcecode>
        <t>The output <tt>response</tt> from this function is wire-encoded into <tt>Nresponse=Ne+Ne+Ns+Nproof</tt> bytes as follows:</t>
        <artwork><![CDATA[
struct {
  uint8 U_enc[Ne];
  uint8 V_enc[Ne];
  uint8 ts_enc[Ns];
  uint8 pi_enc[Nproof];
}
]]></artwork>
        <t>The <tt>U_enc</tt>, <tt>V_enc</tt>, and <tt>ts_enc</tt> fields are the serialized representations
of <tt>response.U</tt>, <tt>response.V</tt>, and <tt>response.ts</tt>, respectively.
The <tt>pi_enc</tt> field is the serialized IssuanceProof, as defined below.</t>
        <t>The CreateIssuanceProof function is defined below.</t>
        <sourcecode type="pseudocode"><![CDATA[
Inputs:
- privateKey:
  - x: Scalar
  - y: Scalar
  - z: Scalar
  - r_x: Scalar
  - r_y: Scalar
- publicKey:
  - Z: Element
  - C_x: Element
  - C_y: Element
  - pi: PublicKeyProof
- hiddenMetadata: Integer
- nBuckets: Integer
- d: Scalar
- U: Element
- V: Element
- ts: Scalar
- T: Element

Output:
- pi: IssuanceProof
  - C: Element
  - e: [Scalar]
  - a: [Scalar]
  - a_d: Scalar
  - a_rho: Scalar
  - a_w: Scalar

Parameters:
- G: Group

def CreateIssuanceProof(privateKey, publicKey, hiddenMetadata, nBuckets, d, U, V, ts, T):
  e_vec = [G.RandomScalar() for i in range(nBuckets)]
  e_vec[hiddenMetadata] = Scalar(0) # zero for now - will be calculated and set later.
  a_vec = [G.RandomScalar() for i in range(nBuckets)]
  a_vec[hiddenMetadata] = Scalar(0) # zero for now - will be calculated and set later.
  r_mu = G.RandomScalar()
  r_d = G.RandomScalar()
  r_rho = G.RandomScalar()
  r_w = G.RandomScalar()
  mu = G.RandomScalar()

  C = hiddenMetadata * publicKey.C_y + mu * G.GeneratorH()
  C_vec = []
  for i in range(nBuckets):
    if i == hiddenMetadata:
      # This is the actual C[i] value for the "correct" slot (i == hiddenMetadata)
      C_vec.append(r_mu * G.GeneratorH())
    else:
      # This is the simulated C[i] value for the "incorrect" slot (i != hiddenMetadata)
      C_vec.append(a_vec[i] * G.GeneratorH() - e_vec[i] * (C - (i * pk.C_y)))
  C_d = r_d * U
  C_rho = (r_d * V) + (r_rho * G.GeneratorH())
  C_w = (r_d * V) + (r_w * G.GeneratorG())

  ser_genG = G.SerializeElement(G.GeneratorG())
  ser_genH = G.SerializeElement(G.GeneratorH())
  ser_C_x = G.SerializeElement(publicKey.C_x)
  ser_C_y = G.SerializeElement(publicKey.C_y)
  ser_Z = G.SerializeElement(publicKey.Z)
  ser_U = G.SerializeElement(U)
  ser_V = G.SerializeElement(V)
  ser_ts = G.SerializeScalar(ts)
  ser_T = G.SerializeElement(T)
  ser_C = G.SerializeElement(C)
  ser_C_d = G.SerializeElement(C_d)
  ser_C_rho = G.SerializeElement(C_rho)
  ser_C_w = G.SerializeElement(C_w)

  challenge_transcript = \
    I2OSP(len(ser_genG), 2) + ser_genG + \
    I2OSP(len(ser_genH), 2) + ser_genH + \
    I2OSP(len(ser_C_x), 2) + ser_C_x + \
    I2OSP(len(ser_C_y), 2) + ser_C_y + \
    I2OSP(len(ser_Z), 2) + ser_Z + \
    I2OSP(len(ser_U), 2) + ser_U + \
    I2OSP(len(ser_V), 2) + ser_V + \
    I2OSP(len(ser_ts), 2) + ser_ts + \
    I2OSP(len(ser_T), 2) + ser_T + \
    I2OSP(len(ser_C), 2) + ser_C
  for i in range(nBuckets):
    ser_C_i = G.SerializeElement(C_vec[i])
    challenge_transcript.append(I2OSP(len(ser_C_i), 2) + ser_C_i)
  challenge_transcript.append( \
    I2OSP(len(ser_C_d), 2) + ser_C_d + \
    I2OSP(len(ser_C_rho), 2) + ser_C_rho + \
    I2OSP(len(ser_C_w), 2) + ser_C_w
  )

  e = G.HashToScalar(challenge_transcript, "TokenResponseProof")
  # Set the correct e_vec[hiddenMetadata] value.
  e_vec[hiddenMetadata] = e - sum(e_vec)

  d_inv = G.ScalarInverse(d)
  rho = -(privateKey.r_x + (hiddenMetadata * privateKey.r_y) + mu)
  w = privateKey.x + (hiddenMetadata * privateKey.y) + (ts * privateKey.z)

  # Set the correct a_vec[hiddenMetadata] value.
  a_vec[hiddenMetadata] = r_mu + (e_vec[hiddenMetadata] * mu)
  a_d = r_d - (e * d_inv)
  a_rho = r_rho + (e * rho)
  a_w = r_w + (e * w)

  return IssuanceProof(C, e_vec, a_vec, a_d, a_rho, a_w)
]]></sourcecode>
        <t>The output <tt>pi</tt> from this function is wire-encoded into <tt>Nproof = Ne+(3+2*nBuckets)*Ns</tt> bytes as follows:</t>
        <artwork><![CDATA[
struct {
  uint8 C_enc[Ne];
  [uint8] e_0_enc[Ns]...e_nBuckets_enc[Ns];
  [uint8] a_0_enc[Ns]...a_nBuckets_enc[Ns];
  uint8 a_d_enc[Ns];
  uint8 a_rho_enc[Ns];
  uint8 a_w_enc[Ns];
}
]]></artwork>
        <t>The fields in this structure are the serialized representations of the contents of <tt>pi</tt>,
e.g., <tt>C_enc</tt> is the serialized representation of <tt>pi.C</tt> and <tt>e_0_enc</tt> is the serialized representation of <tt>e[0]</tt>.</t>
        <section anchor="finalizetoken">
          <name>FinalizeToken</name>
          <t>The FinalizeToken function is defined below. Internally, the client
verifies the token response proof. FinalizeToken fails if this proof
is invalid.</t>
          <artwork><![CDATA[
Inputs:
- context:
  - r: Scalar
  - tc: Scalar
- verifiedPublicKey:
  - Z: Element
  - C_x: Element
  - C_y: Element
- request:
  - T: Element
- response:
  - U: Element
  - V: Element
  - ts: Scalar
  - pi: IssuanceProof
- nBuckets: Integer

Outputs:
- token:
  - t: Scalar
  - P: Element
  - Q: Element

Parameters:
- G: Group

Exceptions:
- VerifyError, raised when response proof verification fails

def FinalizeToken(context, verifiedPublicKey, request, response, nBuckets):
  if VerifyIssuanceProof(verifiedPublicKey, request, response, nBuckets) == false:
    raise VerifyError

  c = G.RandomNonzeroScalar()
  P = c * response.U
  Q = c * (response.V - (context.r * response.U))
  t = context.tc + response.ts

  return token(t, P, Q)
]]></artwork>
          <t>The resulting token can be serialized as the concatenation of <tt>t</tt>, <tt>P</tt>,
and <tt>Q</tt> serialized using their respective serialization functions, yielding
the following struct:</t>
          <artwork><![CDATA[
struct {
  uint8 t_enc[Ns];
  uint8 P_enc[Ne];
  uint8 Q_enc[Ne];
}
]]></artwork>
          <t>The VerifyIssuanceProof function is defined below.</t>
          <sourcecode type="pseudocode"><![CDATA[
Inputs:
- verifiedPublicKey:
  - Z: Element
  - C_x: Element
  - C_y: Element
- T: Element
- response:
  - U: Element
  - V: Element
  - ts: Scalar
  - pi: IssuanceProof
- nBuckets: Integer

Output:
- True if valid, false otherwise

Parameters:
- G: Group

def VerifyIssuanceProof(verifiedPublicKey, T, response, nBuckets):
  pi = response.pi

  C_vec = []
  for i in range(nBuckets):
    C_i = pi.a_vec[i] * G.GeneratorH - (pi.e_vec[i] * (pi.C - (i * verifiedPublicKey.C_y))

  e = sum(pi.e_vec)

  C_d = (response.pi.a_d * response.U) + (e * G.GeneratorG())

  C_rho = (response.pi.a_d * response.V)
      + (response.pi.a_rho * G.GeneratorH())
      + (e * (verifiedPublicKey.C_x + response.pi.C + (response.ts * verifiedPublicKey.Z) + T))

  C_w = (response.pi.a_d * response.V)
      + (response.pi.a_w * G.GeneratorG())
      + (e * T)

  ser_genG = G.SerializeElement(G.GeneratorG())
  ser_genH = G.SerializeElement(G.GeneratorH())
  ser_C_x = G.SerializeElement(verifiedPublicKey.C_x)
  ser_C_y = G.SerializeElement(verifiedPublicKey.C_y)
  ser_Z = G.SerializeElement(verifiedPublicKey.Z)
  ser_U = G.SerializeElement(response.U)
  ser_V = G.SerializeElement(response.V)
  ser_ts = G.SerializeScalar(response.ts)
  ser_T = G.SerializeElement(T)
  ser_C = G.SerializeElement(pi.C)
  ser_C_d = G.SerializeElement(C_d)
  ser_C_rho = G.SerializeElement(C_rho)
  ser_C_w = G.SerializeElement(C_w)

  challenge_transcript = \
    I2OSP(len(ser_genG), 2) + ser_genG + \
    I2OSP(len(ser_genH), 2) + ser_genH + \
    I2OSP(len(ser_C_x), 2) + ser_C_x + \
    I2OSP(len(ser_C_y), 2) + ser_C_y + \
    I2OSP(len(ser_Z), 2) + ser_Z + \
    I2OSP(len(ser_U), 2) + ser_U + \
    I2OSP(len(ser_V), 2) + ser_V + \
    I2OSP(len(ser_ts), 2) + ser_ts + \
    I2OSP(len(ser_T), 2) + ser_T + \
    I2OSP(len(ser_C), 2) + ser_C + \
  for i in range(nBuckets):
    ser_C_i = G.SerializeElement(pi.C_vec[i])
    challenge_transcript.append(I2OSP(len(ser_C_i), 2) + ser_C_i)
  challenge_transcript.append( \
    I2OSP(len(ser_C_d), 2) + ser_C_d + \
    I2OSP(len(ser_C_rho), 2) + ser_C_rho + \
    I2OSP(len(ser_C_w), 2) + ser_C_w
  )

  e_verify = G.HashToScalar(challenge_transcript, "TokenResponseProof")
  return e == e_verify
]]></sourcecode>
        </section>
        <section anchor="verifytoken">
          <name>VerifyToken</name>
          <t>The VerifyToken token is defined below.</t>
          <artwork><![CDATA[
Inputs:
- privateKey:
  - x: Scalar
  - y: Scalar
  - z: Scalar
  - r_x: Scalar
  - r_y: Scalar
- token:
  - t: Scalar
  - P: Element
  - Q: Element
- nBuckets: Integer

Outputs:
- hiddenMetadata: Integer

Parameters:
- G: Group

Exceptions:
- VerifyError, raised when token verification fails

def VerifyToken(privateKey, token, nBuckets):
  if (token.P == token.P + token.P) || (token.Q == token.Q + token.Q):
    raise VerifyError # P and Q should not be zero

  iMatch = -1
  for i in range(nBuckets):
    Q_i = (privateKey.x + token.t * privateKey.z + i * privateKey.y) * token.P
    if token.Q == Q_i:
      if iMatch != -1:
        raise VerifyError # Multiple metadata values match
      iMatch = i

  if iMatch != -1:
    return iMatch
  else:
    raise VerifyError # No metadata values match
]]></artwork>
        </section>
      </section>
    </section>
    <section anchor="security-considerations">
      <name>Security Considerations</name>
      <t>The work of <xref target="CDV22"/> proves the following properties for the ATHM construction presented here:
unforgeability, unlinkability and privacy of the metadata. Unforgeability guarantees that only an
issuer with a valid secret key can generate new tokens. Unlinkability states that tokens with the
same metadata are indistinguishable to the redeemer, i.e. the redeemer does not have any advatage in guessing
which issuance session (among those assigned the same metadata) a paricular redeemed token comes from.
Finally, privacy of the metadata guarantees that any party who does not know the secret verification
key cannot learn any information about the hidden metadata of a token.</t>
      <t>The only place where the construction presented in this document differs from the ATHM construcion presented
in <xref target="CDV22"/> is the fact that we generalize that construction to more than one bit for the hidden metadata.
This requires that the ZK proof provided during issuance to the client proves a new bound on the number of embedded
bits in the token. The proofs of unfogeability and the privacy of the hidden metadata bits do not depend on the
range of the metadata. The unlinkability proof accounts for the fact that the issuer can partition the clients into
a larger than two number of groups, since unlinkability only holds among tokens with the same metadata.</t>
    </section>
    <section anchor="iana-considerations">
      <name>IANA Considerations</name>
      <t>This document has no IANA actions.</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="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="NISTCurves">
          <front>
            <title>Digital signature standard (DSS)</title>
            <author>
              <organization/>
            </author>
            <date year="2013"/>
          </front>
          <seriesInfo name="DOI" value="10.6028/nist.fips.186-4"/>
          <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="CDV22" target="https://eprint.iacr.org/2013/516">
          <front>
            <title>Anonymous Tokens with Stronger Metadata Bit Hiding from Algebraic MACs</title>
            <author>
              <organization/>
            </author>
            <date>n.d.</date>
          </front>
        </reference>
        <reference anchor="RFC7748">
          <front>
            <title>Elliptic Curves for Security</title>
            <author fullname="A. Langley" initials="A." surname="Langley"/>
            <author fullname="M. Hamburg" initials="M." surname="Hamburg"/>
            <author fullname="S. Turner" initials="S." surname="Turner"/>
            <date month="January" year="2016"/>
            <abstract>
              <t>This memo specifies two elliptic curves over prime fields that offer a high level of practical security in cryptographic applications, including Transport Layer Security (TLS). These curves are intended to operate at the ~128-bit and ~224-bit security level, respectively, and are generated deterministically based on a list of required properties.</t>
            </abstract>
          </front>
          <seriesInfo name="RFC" value="7748"/>
          <seriesInfo name="DOI" value="10.17487/RFC7748"/>
        </reference>
      </references>
    </references>
    <?line 955?>

<section numbered="false" anchor="acknowledgments">
      <name>Acknowledgments</name>
      <t>Thanks to Melissa Chase for discussion about the ATHM paper. Thanks also to Tommy Pauly, Phillipp Schoppmann and Ghous Amjad for collaboration on the ATHM specifications.</t>
    </section>
    <section numbered="false" anchor="test-vectors">
      <name>Test Vectors</name>
      <t>This section contains test vectors for the ATHM ciphersuites specified in this document.
The test vectors were generated from the reference implementation located at
https://github.com/google/anonymous-tokens/pull/45/commits/8701431d31c2f49774526fac23bf7a2d24864314.
All byte strings, except <tt>deployment_id</tt>, are encoded in hexadecimal.</t>
      <section numbered="false" anchor="athmp-256-1">
        <name>ATHM(P-256)</name>
        <sourcecode type="JSON"><![CDATA[
[
  {
    "procedure": "params",
    "args": {},
    "output": {
      "deployment_id": "test_vector_deployment_id",
      "generator_g": "036b17d1f2e12c4247f8bce6e563a440f277037d812deb33a0f4a13945d898c296",
      "generator_h": "02361fc6831d3796a82612dffb231ec67253b2f69dbb124c9a0f9917b4e3180d03",
      "n_buckets": "4"
    }
  },
  {
    "procedure": "key_gen",
    "args": {
      "rng_seed": "0101010101010101010101010101010101010101010101010101010101010101"
    },
    "output": {
      "key_id": "027defbe3a76d47f76e8e1296ddbadf8faeb91852a5964d7986ad974441dfc1c",
      "private_key": "023f37203a2476c42566a61cc55c3ca875dbb4cc41c0deb789f8e7bf881836381ecc3686b60ee3b84b6c7d321d70d5c06e9dac63a4d0a79d731b17c0d04d030d01274dd1ee5216c204fb698daea45b52e98b6f0fdd046dcc3a86bb079e36f024147e4b875d59a9ef432b8e45b04a98c4b19dc8c7475f5dce4259b4ca2dd67282b478b8702c1d2569fe52e5d7dbadec6223cd10fd4b504dabac7fff23a37363d1",
      "public_key": "032b80024ee818d709196780a84affe08789f571529fccc1acce07eea2df2fc65f035f6a6eb84bea9c1361119462bda863c47c3b43e53e9e5e7a8796ba1b924d093e03b7f16df0ad82cbd8bd6a04ed427107adf5a3d4ba840e9b341a3badc1d9b7fe19",
      "public_key_proof": "28ccf67d4ad339a45a8435b660160fef113a157221ce6f4a5c6f584a58d4509152852fcc950dd7495211dbefd34b733a730e5a2085e04f847e3c1af4355c59fe"
    }
  },
  {
    "procedure": "token_request",
    "args": {
      "public_key": "032b80024ee818d709196780a84affe08789f571529fccc1acce07eea2df2fc65f035f6a6eb84bea9c1361119462bda863c47c3b43e53e9e5e7a8796ba1b924d093e03b7f16df0ad82cbd8bd6a04ed427107adf5a3d4ba840e9b341a3badc1d9b7fe19",
      "public_key_proof": "28ccf67d4ad339a45a8435b660160fef113a157221ce6f4a5c6f584a58d4509152852fcc950dd7495211dbefd34b733a730e5a2085e04f847e3c1af4355c59fe",
      "rng_seed": "0202020202020202020202020202020202020202020202020202020202020202"
    },
    "output": {
      "token_context": "f6a12ca8ffc30a66ca140ccc7276336115819361186d3f535dd99f8eaaca8fce7f82dd63f4f75c33da444b72372be3aa43c0027a076bf9675eb7932695d127a4",
      "token_request": "030a1b41e492728f4f59d368761e1ff568536ad2987fd5be6017c5043ed25b67ea"
    }
  },
  {
    "procedure": "token_response",
    "args": {
      "hidden_metadata": "3",
      "private_key": "023f37203a2476c42566a61cc55c3ca875dbb4cc41c0deb789f8e7bf881836381ecc3686b60ee3b84b6c7d321d70d5c06e9dac63a4d0a79d731b17c0d04d030d01274dd1ee5216c204fb698daea45b52e98b6f0fdd046dcc3a86bb079e36f024147e4b875d59a9ef432b8e45b04a98c4b19dc8c7475f5dce4259b4ca2dd67282b478b8702c1d2569fe52e5d7dbadec6223cd10fd4b504dabac7fff23a37363d1",
      "public_key": "032b80024ee818d709196780a84affe08789f571529fccc1acce07eea2df2fc65f035f6a6eb84bea9c1361119462bda863c47c3b43e53e9e5e7a8796ba1b924d093e03b7f16df0ad82cbd8bd6a04ed427107adf5a3d4ba840e9b341a3badc1d9b7fe19",
      "rng_seed": "0303030303030303030303030303030303030303030303030303030303030303",
      "token_request": "030a1b41e492728f4f59d368761e1ff568536ad2987fd5be6017c5043ed25b67ea"
    },
    "output": {
      "token_response": "0245db306e323313840a20233ad432aae1be784f96aa940b628d56914249ce4b8c0250ccea1818d4ecf3026ebca1845fbff1cd0089868bbfdecac1c77b9ef2b00480385553aa23a24b14d8bbc2dff606277f444e049797ae7e0404e3a9ba0ecef2fb02d84ab8d7b059374064295a5b2c084c1814a4a33635769a6a196cb99c33122b6affb9e4ee66f5a203f5a31575fd70251b1cd922bcfaa87fc7c071df4412854ba49b424c1a1cded001b3ff8f0bc5306fa9950d236ce7075d9f702c3f0cd5546f8900a60c8dfef9f6633994aa4ed8b1ebd9111bcf05c62b39e9f7426edc7aab3fe679ff5c180ef849b41bd7033353818dd3e442431a1cb2c16992bdadef048a3673a43052fa4d37c9836e498619b9fc1584381292d0b1ec6f952fc6bab8035f6b30478648a01804edaba38b5cf910a932567c48e78b724d57400edd6f98cd565835bb1f733e33dc6335d9283d288ada574131ba288318476415651c571eee8430a75cae55cb231a35b67334ab9c30110d0bc6ce1d3654d4c44bbef74c3d572d432c7b4d06f693bd73505a93a56ab5190b18484c9e7231037fddfa45e1bcbb488def7d54110bbbe0a2a4799a6a8ccff13b1b347fe6867132b1dec0901f7bad11d7c2f8446cffc0f19d60db17c0db5770fdc83f70cd691de8b68289efafac5b098f63"
    }
  },
  {
    "procedure": "finalize_token",
    "args": {
      "public_key": "032b80024ee818d709196780a84affe08789f571529fccc1acce07eea2df2fc65f035f6a6eb84bea9c1361119462bda863c47c3b43e53e9e5e7a8796ba1b924d093e03b7f16df0ad82cbd8bd6a04ed427107adf5a3d4ba840e9b341a3badc1d9b7fe19",
      "rng_seed": "0404040404040404040404040404040404040404040404040404040404040404",
      "token_context": "f6a12ca8ffc30a66ca140ccc7276336115819361186d3f535dd99f8eaaca8fce7f82dd63f4f75c33da444b72372be3aa43c0027a076bf9675eb7932695d127a4",
      "token_request": "030a1b41e492728f4f59d368761e1ff568536ad2987fd5be6017c5043ed25b67ea",
      "token_response": "0245db306e323313840a20233ad432aae1be784f96aa940b628d56914249ce4b8c0250ccea1818d4ecf3026ebca1845fbff1cd0089868bbfdecac1c77b9ef2b00480385553aa23a24b14d8bbc2dff606277f444e049797ae7e0404e3a9ba0ecef2fb02d84ab8d7b059374064295a5b2c084c1814a4a33635769a6a196cb99c33122b6affb9e4ee66f5a203f5a31575fd70251b1cd922bcfaa87fc7c071df4412854ba49b424c1a1cded001b3ff8f0bc5306fa9950d236ce7075d9f702c3f0cd5546f8900a60c8dfef9f6633994aa4ed8b1ebd9111bcf05c62b39e9f7426edc7aab3fe679ff5c180ef849b41bd7033353818dd3e442431a1cb2c16992bdadef048a3673a43052fa4d37c9836e498619b9fc1584381292d0b1ec6f952fc6bab8035f6b30478648a01804edaba38b5cf910a932567c48e78b724d57400edd6f98cd565835bb1f733e33dc6335d9283d288ada574131ba288318476415651c571eee8430a75cae55cb231a35b67334ab9c30110d0bc6ce1d3654d4c44bbef74c3d572d432c7b4d06f693bd73505a93a56ab5190b18484c9e7231037fddfa45e1bcbb488def7d54110bbbe0a2a4799a6a8ccff13b1b347fe6867132b1dec0901f7bad11d7c2f8446cffc0f19d60db17c0db5770fdc83f70cd691de8b68289efafac5b098f63"
    },
    "output": {
      "token": "b7d8310e1899a748b3000e522d320b29880e07119f1a776b639b3ce0a4a01a9f02123d0c125de3d122577b335a8f6616d735e9400b60dcde57eff056e9cbbd2b3c03a062c5b41507e1fe089d0c3b4132d84ece1d4a9dfc0bf4f0588d660f25bdf6cf"
    }
  },
  {
    "procedure": "verify_token",
    "args": {
      "private_key": "023f37203a2476c42566a61cc55c3ca875dbb4cc41c0deb789f8e7bf881836381ecc3686b60ee3b84b6c7d321d70d5c06e9dac63a4d0a79d731b17c0d04d030d01274dd1ee5216c204fb698daea45b52e98b6f0fdd046dcc3a86bb079e36f024147e4b875d59a9ef432b8e45b04a98c4b19dc8c7475f5dce4259b4ca2dd67282b478b8702c1d2569fe52e5d7dbadec6223cd10fd4b504dabac7fff23a37363d1",
      "token": "b7d8310e1899a748b3000e522d320b29880e07119f1a776b639b3ce0a4a01a9f02123d0c125de3d122577b335a8f6616d735e9400b60dcde57eff056e9cbbd2b3c03a062c5b41507e1fe089d0c3b4132d84ece1d4a9dfc0bf4f0588d660f25bdf6cf"
    },
    "output": {
      "hidden_metadata": "3"
    }
  }
]
]]></sourcecode>
      </section>
    </section>
  </back>
  <!-- ##markdown-source:
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
WvCSbtjfscLWqrynshbXTE97/PqreUWm4oM+lKdWkKYGqH0k5uyPhgpX00+N
EbdfB+3vg38Lc4z9qrnZysDEdMhbpWckvOY24epIjnSjwyLibhUUVQt3HYpK
2/CySG3YrKxP9dc+jMdxnl0TCOo9UmZd0gK/fLfp8Hnkq1U6a8Vgbucf9brv
h7qU6jE6iOmj/+Ah9PBzYRWB4M8BVud9VOm+7mw3aP754x29WgCkSftk7PmA
Kagx7mz4eWsSDu/E/0WNXo0/xJ/306wH/xi238H6TzR0vf6gzoSp3qmq9xby
G8UqPlFvsNpa128Kuu80uNm3ajhqNaX1Dlh1PoDZdGydJ4B40x5JeSD7SiNc
btfZqoSx6MzpfkbmqFNE7k9rieolb5SMysgqPWmNUe0urL7qWhJ19/BE1ZNU
PtmwauU9VB2pu4ZURt3XaX9UL++uW+OZ+/tMGb608hc9xR6t50THlfL3Ljoq
z1WQ9sGpzGP9yBcfk71c7OUkXJW9uLsou/+2iqF3eLFs6JkGElvRNJ69R9Os
w08spfv/aIXnsKYOWvByQicP0lIt7hggqa/ZNlLHWNTl8yYNL5q/Uh/WfInC
cx0I7C1BHTSYT/U1RGhZdBtTfMcyFW4/x+24a9Hpb1T3qQga8hpTs8Xfmy1u
91u80J33/kam20L7++7flUUXewUkt1JzptFak2rw9sPin5pPfRcR3/O++wLA
gF8u7NKpkXmvvt+5VGXafRxiqGcAGZ/R/4rP1MrRh+PH8/1Cx4v9S2WxX/m8
a6nquVmqemEvVKlePnadysxx+Jw6rH57YXqtrpTFz1uhaki+c4GKuunQkv8A
CPwYoBu4sUXOcxsvX9i/WMDWDACaC1FNsGOSmwTLE/cH1c+PamGq/ftl3Fq4
yq+z1pWbrqWsfTT9NSDiRFdmKVT5oQ2vavM8hYC8k69XAfKP5qkfmgP9iF70
w96xe49XQLkTOn9gAPBYUWWMNuHR/q5SnwxDr8PQb7xnVPwsYsSvQ0x+ud4d
2uYQH7oB+R66ddN9o3sQ2uOA63tupbEcDmeCp9s7HXh7hOYjsecQ61T9nA6J
pHJ6y8x0SnNP75tWiCWikjbmn/2Q/qiX0kzl60hXgI7cYpWVbq+jS5OqM21D
saUTp3rM466tGlCxVSG7ySjStZZZFyXpZo+W330QLUqN0GGbHjL0+l7vjJYn
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
kTdb/D8WgUXKOpwAAA==

-->

</rfc>
