<?xml version='1.0' encoding='utf-8'?>
<!DOCTYPE rfc [
  <!ENTITY nbsp    "&#160;">
  <!ENTITY zwsp   "&#8203;">
  <!ENTITY nbhy   "&#8209;">
  <!ENTITY wj     "&#8288;">
]>
<!-- name="GENERATOR" content="github.com/mmarkdown/mmark Mmark Markdown Processor - mmark.miek.nl" -->
<rfc xmlns:xi="http://www.w3.org/2001/XInclude" version="3" ipr="trust200902" docName="draft-vasilis-bbs-per-verifier-linkability-01" submissionType="IETF" category="info" xml:lang="en" indexInclude="true" consensus="true">

<front>
<title abbrev="BBS per Verifier Linkability">BBS per Verifier Linkability</title><seriesInfo value="draft-vasilis-bbs-per-verifier-linkability-01" status="informational" name="Internet-Draft"/>
<author initials="V." surname="Kalos" fullname="Vasilis Kalos"><organization>MATTR</organization><address><postal><street/>
</postal><email>vasilis.kalos@mattr.global</email>
</address></author><author initials="G." surname="Bernstein" fullname="Greg Bernstein"><organization>Grotto Networking</organization><address><postal><street/>
</postal><email>gregb@grotto-networking.com</email>
</address></author><date/>
<area>Internet</area>
<workgroup>none</workgroup>

<abstract>
<t>The BBS Signatures scheme defined in <xref target="I-D.irtf-cfrg-bbs-signatures"/>, describes a multi-message digital signature, that supports selectively disclosing the messages through unlinkable presentations, built using zero-knowledge proofs. Each BBS proof reveals no information other than the signed messages that the Prover chooses to disclose in that specific instance. As such, the Verifier (i.e., the recipient) of the BBS proof, may not be able to track those presentations over time. Although in many applications this is desirable, there are use cases where that require the Verifier be able to track the BBS proofs they receive from the same Prover. Examples include monitoring the use of access credentials for abnormal activity, monetization etc.. This document presents the use of pseudonyms with BBS proofs.</t>
<t>A pseudonym, is a value that will remain constant each time a Prover presents a BBS proof to the same Verifier, but will be different (and unlinkable), when the Prover interacts with a different Verifier. This provides a way for a recipient (Verifier) to track the presentations intended for them, while also hindering them from tracking the Prover's interactions with other Verifiers.</t>
</abstract>

</front>

<middle>

<section anchor="introduction"><name>Introduction</name>
<t>The BBS Signature Scheme, originally described in the academic work by Dan Boneh, Xavier Boyen, and Hovav Shacham <xref target="BBS04"/>, is a signature scheme able to sign multiple messages at once, allowing for selectively disclosing those message while not revealing the signature it self. It does so by creating unlinkable, zero-knowledge proofs-of-knowledge of a signature value on (among other) the disclosed set of messages. More specifically, the BBS Prover, will create a BBS proof that if validated by the Verifier, guarantees that the prover knows a BBS signature on the disclosed messages, guaranteeing the revealed messages authenticity and integrity.</t>
<t>The BBS Proof is by design unlinkable, meaning that given two different BBS proofs, there is no way to tell if they originated from the same BBS signature. This means that if a Prover does not reveal any other identifying information (for example if they are using proxies to hide their IP address etc.), the Verifier of the proof will not be able "track" or "correlate" the different proof presentations  or the Provers activity via cryptographic artifacts. This helps enhance user privacy in applications where the Verifier only needs to know that the Prover is in possession of a valid BBS signature over a list of disclosed messages.</t>
<t>In some applications, however, the Verifier needs to track the presentations made by the Prover over time, as to provide security monitoring, monetization services, configuration persistance etc.. To promote privacy reason, the Prover should not reveal or be bound to a unique identifier that would remain constant across proof presentations to different Verifiers and which could be used to link a Provers interactions with different Verifiers.</t>
<t>The goal of this document is to provide a way for a Verifier to track the proof presentations that are intended for them, while at the same time not allowing the tracking of the Prover's activities with other Verifiers. This is done through the use of Pseudonyms. A pseudonym as defined by this document, is a value that will be constant when the Prover presents BBS proofs to the same Verifier, but will change when the Prover interacts with different recipients (with no way to link the two distinct pseudonym values together). This is done by constructing the pseudonym value by combining a unique Verifier identifier with a unique Prover identifier.</t>
<t>To avoid forging requests, the Prover's identifier will be signed by the same BBS signature used to generate the BBS proof. This requires extending the BBS proof generation and verification operations with some additional computations that will be used to prove correctness of the pseudonym, i.e., that it was correctly calculated using the Verifier identifier, as well as, the undisclosed and signed Prover identifier. The Prover identifier MUST be considered secret from the point of view of the Prover, since, if it is revealed, any entity will be able to track the Prover's activity across any Verifiers.</t>
<t>This document will define new BBS Interfaces for use with pseudonyms, however it will not define new ciphersuites. Rather it will re-use the ciphersuites defined in <eref target="https://www.ietf.org/archive/id/draft-irtf-cfrg-bbs-signatures-03.html#name-ciphersuites">Section 6</eref> of <xref target="I-D.irtf-cfrg-bbs-signatures"/>).</t>
<t>Pseudonyms when used appropriately prevent verifiers from linking prover (proof) presentations between them. We call this verifier-verifier collusion. In addition pseudonyms can be used to prevent the signer from linking prover presentations to a verifier. We call this verifier-signer collusion. This second property is not always desirable in all use cases, for example to allow tracking of purchases a controlled substance by a prover by a central authority while preventing tracking by individual shops.</t>
<t>This specification provides for pseudonyms that provide for both use cases above. We call the case that prevents linkage across verifiers and signer <em>psuedonyms with hidden pid</em> while the case that allows for signer linkage <em>pseudonyms with signer provided pid</em>.</t>

<section anchor="terminology"><name>Terminology</name>
<t>The following terminology is used throughout this document:</t>

<dl spacing="compact">
<dt>SK</dt>
<dd>The secret key for the signature scheme.</dd>
<dt>PK</dt>
<dd>The public key for the signature scheme.</dd>
<dt>L</dt>
<dd>The total number of signed messages.</dd>
<dt>R</dt>
<dd>The number of message indexes that are disclosed (revealed) in a proof-of-knowledge of a signature.</dd>
<dt>U</dt>
<dd>The number of message indexes that are undisclosed in a proof-of-knowledge of a signature.</dd>
<dt>scalar</dt>
<dd>An integer between 0 and r-1, where r is the prime order of the selected groups, defined by each ciphersuite (see also <eref target="#notation">Notation</eref>).</dd>
<dt>generator</dt>
<dd>A valid point on the selected subgroup of the curve being used that is employed to commit a value.</dd>
<dt>signature</dt>
<dd>The digital signature output.</dd>
<dt>presentation_header (ph)</dt>
<dd>A payload generated and bound to the context of a specific spk.</dd>
<dt>INVALID, ABORT</dt>
<dd>Error indicators. INVALID refers to an error encountered during the Deserialization or Procedure steps of an operation. An INVALID value can be returned by a subroutine and handled by the calling operation. ABORT indicates that one or more of the initial constraints defined by the operation are not met. In that case, the operation will stop execution. An operation calling a subroutine that aborted must also immediately abort.</dd>
</dl>
</section>

<section anchor="notation"><name>Notation</name>
<t>The following notation and primitives are used:</t>

<dl spacing="compact">
<dt>a || b</dt>
<dd>Denotes the concatenation of octet strings a and b.</dd>
<dt>I \ J</dt>
<dd>For sets I and J, denotes the difference of the two sets i.e., all the elements of I that do not appear in J, in the same order as they were in I.</dd>
<dt>X[a..b]</dt>
<dd>Denotes a slice of the array <tt>X</tt> containing all elements from and including the value at index <tt>a</tt> until and including the value at index <tt>b</tt>. Note when this syntax is applied to an octet string, each element in the array <tt>X</tt> is assumed to be a single byte.</dd>
<dt>range(a, b)</dt>
<dd>For integers a and b, with a &lt;= b, denotes the ascending ordered list of all integers between a and b inclusive (i.e., the integers "i" such that a &lt;= i &lt;= b).</dd>
<dt>length(input)</dt>
<dd>Takes as input either an array or an octet string. If the input is an array, returns the number of elements of the array. If the input is an octet string, returns the number of bytes of the inputted octet string.</dd>
</dl>
<t>Terms specific to pairing-friendly elliptic curves that are relevant to this document are restated below, originally defined in <xref target="I-D.irtf-cfrg-pairing-friendly-curves"/>.</t>

<dl spacing="compact">
<dt>E1, E2</dt>
<dd>elliptic curve groups defined over finite fields. This document assumes that E1 has a more compact representation than E2, i.e., because E1 is defined over a smaller field than E2. For a pairing-friendly curve, this document denotes operations in E1 and E2 in additive notation, i.e., P + Q denotes point addition and x * P denotes scalar multiplication.</dd>
<dt>G1, G2</dt>
<dd>subgroups of E1 and E2 (respectively) having prime order r.</dd>
<dt>GT</dt>
<dd>a subgroup, of prime order r, of the multiplicative group of a field extension.</dd>
<dt>e</dt>
<dd>G1 x G2 -&gt; GT: a non-degenerate bilinear map.</dd>
<dt>r</dt>
<dd>The prime order of the G1 and G2 subgroups.</dd>
<dt>BP1, BP2</dt>
<dd>base (constant) points on the G1 and G2 subgroups respectively.</dd>
<dt>Identity_G1, Identity_G2, Identity_GT</dt>
<dd>The identity element for the G1, G2, and GT subgroups respectively.</dd>
<dt>hash_to_curve_g1(ostr, dst) -&gt; P</dt>
<dd>A cryptographic hash function that takes an arbitrary octet string as input and returns a point in G1, using the hash_to_curve operation defined in <xref target="I-D.irtf-cfrg-hash-to-curve"/> and the inputted dst as the domain separation tag for that operation (more specifically, the inputted dst will become the DST parameter for the hash_to_field operation, called by hash_to_curve).</dd>
<dt>point_to_octets_g1(P) -&gt; ostr, point_to_octets_g2(P) -&gt; ostr</dt>
<dd>returns the canonical representation of the point P for the respective subgroup as an octet string. This operation is also known as serialization.</dd>
<dt>octets_to_point_g1(ostr) -&gt; P, octets_to_point_g2(ostr) -&gt; P</dt>
<dd>returns the point P for the respective subgroup corresponding to the canonical representation ostr, or INVALID if ostr is not a valid output of the respective point_to_octets_g* function. This operation is also known as deserialization.</dd>
</dl>
</section>
</section>

<section anchor="conventions-and-definitions"><name>Conventions and Definitions</name>
<t>The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD", "SHOULD NOT", "RECOMMENDED", "NOT RECOMMENDED", "MAY", and "OPTIONAL" in this document are to be interpreted as described in BCP 14 <xref target="RFC2119"/> <xref target="RFC8174"/> when, and only when, they appear in all capitals, as shown here.</t>
</section>

<section anchor="key-concepts"><name>Key Concepts</name>
<t>A <em>pseudonym</em> will be cryptographically generated for each prover-verifier pair. Its value is dependent on a <em>prover identifier</em> (<em>pid</em>) and a verifier identifier (<em>verifier_id</em>).</t>

<section anchor="prover-identifier"><name>Prover Identifier</name>
<t>A Prover Identifier (<tt>pid</tt>), is an octet string that MUST be at least 32 octets long. This is a value that MUST never be revealed to a verifier.  It MUST be indistinguishable from a random value, drawn from the uniform distribution over the space of all octet strings that are at least 32 octets long. Such value could be generated from a cryptographically secure pseudo-random number generator. See <xref target="DRBG"/> for requirements and suggestions on generating randomness.</t>
<t>In the case of <em>pseudonym with hidden pid</em> the Prover Identifier is generated by the <em>prover</em> and this value is never revealed to the signer or any other entity.</t>
<t>In the case of <em>pseudonym with signer provided pid</em> the Prover Identifier is chosen by the BBS Signer. This gives the Signer the ability to track the Prover even when they present BBS proofs with pseudonyms to different Verifiers. It also MUST unique across different Provers with very high probability. This value MUST be securely conveyed from the signer from the prover. The mechanism for doing this is application specific and not specified here.</t>
</section>

<section anchor="verifier-identifier"><name>Verifier Identifier</name>
<t>The Verifier Identifier (<em>verifier_id</em>) is an octet string that SHOULD be unique to a verifier and is generated by a verifier. Unlike a <em>pid</em> this value does not need to be kept secret from other entities. In fact, this value must be known to all provers that will be presenting proofs to the verifier. The mechanism for conveying the <em>verifier_id</em> to the prover is application specific and not specified here.</t>
<t>In addition, as we will see in the computation of a pseudonym, this value gets hashed into the group G1, and hence doesn't have the same randomness requirements as a <em>pid</em>. Hence, well known, unique verifier associated information such as a domain name could be used as the basis of a verifier id, but this specification does not dictate any particular format.</t>
</section>

<section anchor="pseudonyms"><name>Pseudonyms</name>
<t>The <em>pseudonym</em> is a cryptographic value computed by the prover based on the <em>pid</em> and the <em>verifier_id</em>. At a high level this is computed by hashing the <em>verifier_id</em> to the elliptic curve group G1 and then exponentiating by the <em>pid</em> value. See section <xref target="calculate-pseudonym"/> for details. The <em>pseudonym</em> is sent to a verifier along with the proof.</t>
<t>This document defines a pseudonym as point of the G1 group different from the Identity (<tt>Identity_G1</tt>) or the base point (<tt>BP1</tt>) of G1. A pseudonym remains constant for each Prover and Verifier pair, but is unique (and unlinkable) across different Provers or Verifiers. In other words, when the Prover presents multiple BBS proofs with a pseudonym to a Verifier, the pseudonym value will be constant across those presentations. When presenting a BBS proof with a pseudonym to another Verifier however, the pseudonym value will be different. Note that since pseudonyms are group points, their value will necessarily change if a different a ciphersuite with a different curve will be used. Serialization and deserialization of the pseudonym point MUST be done using the <tt>point_to_octets_g1</tt> and <tt>octets_to_point_g1</tt> defined by the BBS ciphersuite used (see <eref target="https://www.ietf.org/archive/id/draft-irtf-cfrg-bbs-signatures-03.html#name-ciphersuites">Section 6</eref> of <xref target="I-D.irtf-cfrg-bbs-signatures"/>).</t>
<t>This document specifies pseudonyms to be BBS Interface specific (see Section TBD of <xref target="I-D.irtf-cfrg-bbs-signatures"/> for the definition of the BBS Interface). It is outside the scope of this document to provide a procedure for "linking" the pseudonyms that are used by different Interfaces or that are based on different ciphersuites. An option is for the Prover to present both Pseudonyms with the relevant BBS proofs to the Verifier, and upon validation of both, the Verifier to internally link the 2 pseudonyms together.</t>
</section>

<section anchor="mapping-messages-to-scalars"><name>Mapping Messages to Scalars</name>
<t>Each BBS Interface defines an operation that will map the inputted messages to scalar values, required by the core BBS operations. Each Interface can use a different mapping procedure, as long as it comforts to the requirements outlined in <xref target="I-D.irtf-cfrg-bbs-signatures"/>. For using BBS with pseudonyms, the mapping operation used by the interface is REQUIRED to additionally adhere the following rule;</t>

<artwork>For each set of messages and separate message msg',
if C1 = messages_to_scalars(messages.push(msg')),
and msg_prime_scalar = messages_to_scalars((msg')),
and C2 = messages_to_scalars(messages).push(msg_prime_scalar),
it will always hold that C1 == C2.
</artwork>
<t>Informally, the above means that each message is mapped to a scalar independently from all the other messages. For example, if <tt>a = messages_to_scalars((msg_1))</tt> and <tt>b = messages_to_scalars((msg_2))</tt>, then <tt>(a, b) = messages_to_scalars((msg_1, msg_2))</tt>. Its trivial to see that the <tt>messages_to_scalars</tt> operation that is defined in Section TBD of <xref target="I-D.irtf-cfrg-bbs-signatures"/>, has the required property. That operation will be used by the Interface defined in this document to map the messages to scalars. Note that the above operation (and hence the defined by this document Interface), only accepts messages that are octet strings.</t>
</section>
</section>

<section anchor="high-level-procedures-and-information-flows"><name>High Level Procedures and Information Flows</name>
<t>To prevent forgeries in all cases all BBS messages are signed with the inclusion of some form of the provider identifier (<em>pid</em>). In addition the pseudonym is always computed by the prover and sent with the proof to the verifier. While two different variations of signature and proof generation are given below based on the previously discussed unlinkability requirements there MUST be only one verification algorithm for the verifier to use.</t>

<section anchor="pseudonym-with-hidden-pid"><name>Pseudonym with Hidden Pid</name>

<ol spacing="compact">
<li>In this case the <em>pid</em> is computed by the prover and retained for use in generating proofs.</li>
<li>The <em>pid</em> and is wrapped up in a cryptographic commitment using the <em>Commit</em> procedures of Blind BBS which returns a <em>commitment_with_proof</em> value and a <em>secret_prover_blind</em>.</li>
<li>The <em>commitment_with_proof</em> is conveyed to the signer which then uses the signing procedures in section <xref target="hidden-pid-signature-generation-and-verification"/> to create a BBS Pseudonym Signature which is conveyed to the prover along with possibly and optional <em>signer_blind</em>.</li>
<li>On receipt of the signature the prover verifies the signature using the procedure of section <xref target="hidden-pid-signature-generation-and-verification"/>.</li>
<li>The prover computes the <em>pseudonym</em> based on the <em>pid</em> and <em>verifier_id</em></li>
<li>The prover generates a proof using <em>pid</em>, <em>secret_prover_blind</em>, <em>signature</em> and <em>signer_blind</em> (if provided with the signature) using the procedures of section <xref target="hidden-pid-proof-generation-with-pseudonym"/>.</li>
<li>The prover conveys the <em>proof</em> and <em>pseudonym</em> to the verifier. The verifier uses the procedure of section <xref target="proof-verification-with-pseudonym"/> to verify the proof.</li>
</ol>
</section>

<section anchor="pseudonym-with-signer-provided-pid"><name>Pseudonym with Signer provided Pid</name>

<ol spacing="compact">
<li>In this case the <em>pid</em> is computed by the signer and may need to be retained if the signer is to produce more signatures for the same prover.</li>
<li>The signer used the <em>pid</em> in producing a BBS Pseudonym Signature according to section <xref target="signer-provided-pid-signature-generation-and-verification"/> for the prover. The signature and the <em>pid</em> are conveyed to the prover in a secure fashion, i.e., the <em>pid</em> can be considered sensitive information.</li>
<li>The prover on receipt of the signature and <em>pid</em> verifiers the signature using the procedure of section <xref target="signer-provided-pid-signature-generation-and-verification"/>.</li>
<li>The prover computes the <em>pseudonym</em> based on the <em>pid</em> and <em>verifier_id</em></li>
<li>The prover generates a proof using <em>pid</em>, and <em>signature</em> based on the procedure of section <xref target="signer-provided-pid-proof-generation-with-pseudonym"/>.</li>
<li>The prover conveys the <em>proof</em> and <em>pseudonym</em> to the verifier. The verifier uses the procedure of section <xref target="proof-verification-with-pseudonym"/> to verify the proof.</li>
</ol>
</section>

<section anchor="proof-verification-integration-and-participants-knowledge"><name>Proof Verification Integration and Participants Knowledge</name>
<t>Note that we use a three party model of <em>signer</em>, <em>prover</em>, and <em>verifier</em>. In the terminology commonly encountered with <em>credentials</em> these would correspond to <em>issuer</em>, <em>holder</em>, and <em>verifier</em>.</t>
<t>As will be seen in the following content the two different cases of <em>signer provided pid</em> and <em>hidden pid</em> are clear to the <em>signer</em> since it has two very different procedures in the two cases. Hence a <em>signer</em> (<em>issuer</em>) will always know which it is involved with.</t>
<t>Similarly from the <em>provers</em> perspective they have very different procedures and retained information for generating proofs in the two different cases.</t>
<t>In the following we have unified the verification procedure for the two cases. In the case of <em>signer provided pid</em> where there is, say statutory reporting, from a <em>verifier</em> to the <em>signer</em>, e.g., for controlled substance purchase monitoring, the <em>verifier</em> would know that its involved in the <em>signer provided pid</em> case since the <em>hidden pid</em> case is unlinkable under <em>verifier-to-signer</em> collusion.</t>
<t>Hence, in both use-cases it seems that the parties involved have relatively complete information concerning their privacy, particularly the <em>prover</em> (<em>holder</em>) and that having a common verification procedure for both cases would simplify protocol implementation and promotes acceptance.</t>
</section>
</section>

<section anchor="general-procedures"><name>General Procedures</name>
<t>This section defines general procedures that are independent of whether a signer provided pid or a prover hidden pid is used. This includes operations for generating a pseudonym.</t>

<section anchor="calculate-pseudonym"><name>Calculate Pseudonym</name>
<t>The following operation describes how to calculate a pseudonym from the Prover's and the Verifier's unique identifiers (IDs), as well as a BBS Interface identifier (<tt>api_id</tt>, see TBD). The pseudonym will be unique for different Verifier and interface IDs and constant under constant inputs (i.e., the same <tt>verifier_id</tt>, <tt>pid</tt> and <tt>api_id</tt> values).</t>

<artwork>pseudonym = CalculatePseudonym(verifier_id, pid, api_id)

Inputs:

- verifier_id (REQUIRED), an octet string, representing the unique proof
                          Verifier identifier.
- pid (REQUIRED), an octet string, representing the unique Prover
                  identifier.
- api_id (OPTIONAL), an octet string. If not supplied it defaults to the
                     empty octet string ("").


Outputs:

- pseudonym, A point of G1, different from the Identity_G1, BP1 and P1
             (see the Parameters of this operation); or INVALID.

Parameters:

- hash_to_curve_g1, the hash_to_curve operation defined by the Hash to
                    Curve suite determined by the ciphersuite, through
                    the hash_to_curve_suite parameter.
- P1, fixed point of G1, defined by the ciphersuite.

Procedure:

1. OP = hash_to_curve_g1(verifier_id, api_id)
2. if OP is INVALID, return INVALID
3. if OP == Identity_G1 or OP == BP1 or OP == P1, return INVALID
4. pid_scalar = messages_to_scalars((pid), api_id)
5. return OP * pid_scalar
</artwork>
</section>
</section>

<section anchor="signer-provided-pid-bbs-pseudonym-interface"><name>Signer Provided PID BBS Pseudonym Interface</name>
<t>This is the signer (issuer) known <em>pid</em> case.</t>
<t>The following section defines a BBS Interface that will make use of per-origin pseudonyms. The identifier of the Interface is defined as <tt>ciphersuite_id || H2G_HM2S_PSEUDONYM_</tt>, where <tt>ciphersuite_id</tt> the unique identifier of the BBS ciphersuite used, as is defined in <eref target="https://www.ietf.org/archive/id/draft-irtf-cfrg-bbs-signatures-03.html#name-ciphersuites">Section 6</eref> of <xref target="I-D.irtf-cfrg-bbs-signatures"/>). Each BBS Interface MUST define operations to map the inputted messages to scalar values and to create the generators set, required by the core operations. The inputted messages to the defined in this document BBS Interface will be mapped to scalars using the <tt>messages_to_scalars</tt> operation defined in Section TBD of <xref target="I-D.irtf-cfrg-bbs-signatures"/>. The generators will be created using the <tt>create_generators</tt> operation defined in Section TBD of <xref target="I-D.irtf-cfrg-bbs-signatures"/>.</t>
<t>This document also defines two alternative core proof generation and verification operations (see <xref target="core-operations"/>), to accommodate the use of pseudonyms. Those operations will be used by the defined proof generation and verification Interface operations, in place of the <tt>CoreProofGen</tt> and <tt>CoreProofVerify</tt> operations defined in Section TBD of <xref target="I-D.irtf-cfrg-bbs-signatures"/>.</t>

<section anchor="signer-provided-pid-signature-generation-and-verification"><name>Signer Provided PID Signature Generation and Verification</name>
<t>The Issuer of the BBS signature will include a constant unique prover identifier (<tt>pid</tt>) as one of the signed messages. The format of that identifier is outside the scope of this document. An option is to use a pseudo random generator to return 32 random octets. The <tt>pid</tt> value MUST be the last one in the set of signed messages.</t>
<t>More specifically, the Signer to generate a signature from a secret key (SK), a constant Prover identifier (<tt>pid</tt>) and optionally over a <tt>header</tt> and or a vector of <tt>messages</tt>, MUST execute the following steps,</t>

<artwork>1. messages = messages.push(pid)
2. commitment_with_proof = "" // The empty string
2. signature = BlindSign(SK, PK, commitment_with_proof, header,
                                                               messages)
</artwork>
<t>Where <tt>BlindSign</tt> is defined in <eref target="https://www.ietf.org/archive/id/draft-kalos-bbs-blind-signatures-00.html#name-blind-signature-generation">Section 4.2.1</eref> of <xref target="I-D.irtf-cfrg-bbs-signatures"/>, instantiated with the <tt>api_id</tt> parameter set to the value <tt>ciphersuite_id || H2G_HM2S_PSEUDONYM_</tt>, where <tt>ciphersuite_id</tt> the unique identifier of the ciphersuite.</t>
<t>To verify the above <tt>signature</tt>, for a given <tt>pid</tt>, <tt>header</tt> and vector of <tt>messages</tt>, against a supplied public key (<tt>PK</tt>), the Prover MUST execute the following steps,</t>

<artwork>1. messages = messages.push(pid)
2. signature = Verify(PK, signature, header, messages)
</artwork>
<t>The <tt>Verify</tt> operation is defined in <eref target="https://www.ietf.org/archive/id/draft-kalos-bbs-blind-signatures-00.html#name-blind-signature-verificatio">Section 4.2.2</eref> of <xref target="I-D.irtf-cfrg-bbs-signatures"/>, instantiated with the <tt>api_id</tt> parameter set to the value <tt>ciphersuite_id || H2G_HM2S_PSEUDONYM_</tt>, where <tt>ciphersuite_id</tt> the unique identifier of the ciphersuite.</t>
</section>

<section anchor="signer-provided-pid-proof-generation-with-pseudonym"><name>Signer Provided PID Proof Generation with Pseudonym</name>
<t>This section defines operations for calculating a BBS proof with a pseudonym in the signer provided pid case. The BBS proof is extended to include a zero-knowledge proof of correctness of the pseudonym value, i.e., that is correctly calculated using the (undisclosed) id of the Prover (<tt>pid</tt>), and that is "bound" to the underlying BBS signature (i.e., that the <tt>pid</tt> value is signed by the Signer).</t>

<section anchor="signer-provided-pid-proof-generation"><name>Signer Provided PID Proof Generation</name>
<t>This operation computes a BBS proof with a pseudonym, which is a zero-knowledge, proof-of-knowledge, of a BBS signature, while optionally disclosing any subset of the signed messages. The BBS proof is extended to also include a zero-knowledge proof of correctness of the pseudonym, meaning that it is correctly calculated, using a signed Prover identifier and the supplied Verifier's ID.</t>
<t>Validating the proof (see <tt>ProofVerifyWithPseudonym</tt> defined in <xref target="proof-verification-with-pseudonym"/>), guarantees authenticity and integrity of the header, presentation header and disclosed messages, knowledge of a valid BBS signature as well as correctness and ownership of the pseudonym.</t>
<t>This operation makes use of <tt>CoreProofGenWithPseudonym</tt> as defined in <xref target="core-proof-generation"/>.</t>

<artwork>proof = ProofGenWithPseudonym(PK,
                              signature,
                              Pseudonym,
                              verifier_id,
                              pid,
                              header,
                              ph,
                              messages,
                              disclosed_indexes)

Inputs:

- PK (REQUIRED), an octet string of the form outputted by the SkToPk
                 operation.
- signature (REQUIRED), an octet string of the form outputted by the
                        Sign operation.
- Pseudonym (REQUIRED), A point of G1, different from the Identity of
                        G1, as outputted by the CalculatePseudonym
                        operation.
- verifier_id (REQUIRED), an octet string, representing the unique proof
                          Verifier identifier.
- pid (REQUIRED), an octet string, representing the unique Prover
                  identifier.
- header (OPTIONAL), an octet string containing context and application
                     specific information. If not supplied, it defaults
                     to an empty string.
- ph (OPTIONAL), an octet string containing the presentation header. If
                 not supplied, it defaults to an empty string.
- messages (OPTIONAL), a vector of octet strings. If not supplied, it
                       defaults to the empty array "()".
- disclosed_indexes (OPTIONAL), vector of unsigned integers in ascending
                                order. Indexes of disclosed messages. If
                                not supplied, it defaults to the empty
                                array "()".

Parameters:

- api_id, the octet string ciphersuite_id || "H2G_HM2S_PSEUDONYM_",
          where ciphersuite_id is defined by the ciphersuite and
          "H2G_HM2S_PSEUDONYM_" is an ASCII string comprised of
          9 bytes.

Outputs:

- proof, an octet string; or INVALID.

Procedure:

1. messages.append(pid) // add pid to end of messages
2. message_scalars = messages_to_scalars(messages, api_id)
3. pid_scalar = messages_to_scalars((pid), api_id)

// includes one for pid
4. generators = create_generators(length(messages) + 1, api_id)

5. proof = CoreProofGenWithPseudonym(PK,
                                     signature,
                                     Pseudonym,
                                     verifier_id,
                                     generators,
                                     header,
                                     ph,
                                     message_scalars,
                                     disclosed_indexes,
                                     api_id)

6. if proof is INVALID, return INVALID
7. return proof
</artwork>
</section>
</section>
</section>

<section anchor="hidden-pid-bbs-pseudonym-interface"><name>Hidden PID BBS Pseudonym Interface</name>
<t>This is the prover generated, hidden <em>pid</em> case.</t>
<t>The following section defines a BBS Interface that will make use of per-origin pseudonyms where the <em>pid</em> value is only known to the prover. The identifier of the Interface, api_id,  is defined as <tt>ciphersuite_id || H2G_HM2S_PSEUDONYM_</tt>, where <tt>ciphersuite_id</tt> the unique identifier of the BBS ciphersuite used, as is defined in <eref target="https://www.ietf.org/archive/id/draft-irtf-cfrg-bbs-signatures-03.html#name-ciphersuites">Section 6</eref> of <xref target="I-D.irtf-cfrg-bbs-signatures"/>).</t>
<t>In this case the prover create a pid value and keeps it secret. Only sending a commitment with the proof of the pid that the signer will used when creating the signature.</t>

<section anchor="hidden-pid-signature-generation-and-verification"><name>Hidden PID Signature Generation and Verification</name>
<t>The prover will create a commitment on its pid and conveys it the the signer using the following steps from <xref target="I-D.kalos-bbs-blind-signatures"/>:</t>

<artwork>1. committed_messages = [pid]
2. (commitment_with_proof, secret_prover_blind) = Commit(
                                                   committed_messages,
                                                   api_id)
3. convey commitment_with_proof to Signer.
</artwork>
<t>The Signer generate a signature from a secret key (SK), a the commitment with proof,
and optionally over a <tt>header</tt>, a vector of <tt>messages</tt>, and optional <tt>signer_blind</tt> using
the BlindSign procedure from <xref target="I-D.kalos-bbs-blind-signatures"/>.</t>

<artwork>1. blind_signature = BlindSign(SK, PK, commitment_with_proof, header,
                                                 messages, signer_blind)
</artwork>
<t>To verify the above <tt>signature</tt>, the prover uses the <tt>header</tt>, vector of <tt>messages</tt> and <tt>signer_blind</tt> (if used by the signer) along with its <tt>pid</tt> value, and retained
<tt>secret_prover_blind</tt> value from the commitment generation. According to the following
steps:</t>

<artwork>1. committed_messages = [pid]
2. result = BlindVerify(PK, blind_signature, header, messages,
                  committed_messages, secret_prover_blind, signer_blind)
</artwork>
</section>

<section anchor="hidden-pid-proof-generation-with-pseudonym"><name>Hidden PID Proof Generation with Pseudonym</name>
<t>This section defines operations for calculating a BBS proof with a pseudonym in the hidden pid case. The BBS proof is extended to include a zero-knowledge proof of correctness of the pseudonym value, i.e., that is correctly calculated using the (undisclosed) id of the Prover (<tt>pid</tt>), and that is "bound" to the underlying BBS signature (i.e., that the <tt>pid</tt> value is signed by the Signer).</t>
<t>Validating the proof (see <tt>HiddenPidProofVerifyWithPseudonym</tt> defined in <xref target="proof-verification-with-pseudonym"/>), guarantees authenticity and integrity of the header, presentation header and disclosed messages, knowledge of a valid BBS signature as well as correctness and ownership of the pseudonym.</t>
<t>This operation makes use of <tt>HiddenPidCoreProofGenWithPseudonym</tt> as defined in <xref target="core-proof-generation"/>.</t>

<artwork>proof = HiddenPidProofGenWithPseudonym(PK,
                              signature,
                              Pseudonym,
                              verifier_id,
                              pid,
                              header,
                              ph,
                              messages,
                              disclosed_indexes,
                              secret_prover_blind,
                              signer_blind)

Inputs:

- PK (REQUIRED), an octet string of the form outputted by the SkToPk
                 operation.
- signature (REQUIRED), an octet string of the form outputted by the
                        Sign operation.
- Pseudonym (REQUIRED), A point of G1, different from the Identity of
                        G1, as outputted by the CalculatePseudonym
                        operation.
- verifier_id (REQUIRED), an octet string, representing the unique proof
                          Verifier identifier.
- pid (REQUIRED), an octet string, representing the unique Prover
                  identifier.
- header (OPTIONAL), an octet string containing context and application
                     specific information. If not supplied, it defaults
                     to an empty string.
- ph (OPTIONAL), an octet string containing the presentation header. If
                 not supplied, it defaults to an empty string.
- messages (OPTIONAL), a vector of octet strings. If not supplied, it
                       defaults to the empty array "()".
- disclosed_indexes (OPTIONAL), vector of unsigned integers in ascending
                                order. Indexes of disclosed messages. If
                                not supplied, it defaults to the empty
                                array "()".
- secret_prover_blind, a scalar value, retained from the commit
                       procedure.
- signer_blind (OPTIONAL), a scalar value. If not supplied it defaults
                           to zero "0".

Parameters:

- api_id, the octet string ciphersuite_id || "H2G_HM2S_PSEUDONYM_",
          where ciphersuite_id is defined by the ciphersuite and
          "H2G_HM2S_PSEUDONYM_" is an ASCII string comprised of
          9 bytes.

Outputs:

- proof an octet string, or INVALID.

Parameters:

- api_id, the octet string ciphersuite_id || "BLIND_H2G_HM2S_", where
          ciphersuite_id is defined by the ciphersuite and
          "BLIND_H2G_HM2S_"is an ASCII string composed of 15 bytes.



Deserialization:

1. L = length(messages)
2. if length(disclosed_indexes) &gt; L, return INVALID
3. for i in disclosed_indexes, if i &lt; 0 or i &gt;= L, return INVALID
4. for j in disclosed_commitment_indexes,
                               if i &lt; 0 or i &gt;= L, return INVALID

Procedure:

1.  message_scalars = ()
2.  message_scalars.append(BBS.messages_to_scalars(messages, api_id))

3.  blind_factor = secret_prover_blind + signer_blind
4.  message_scalars.append(blind_factor)

5.  message_scalars.append(BBS.messages_to_scalars(
                                   [pid], api_id))


6.  generators = BBS.create_generators(L + 1, api_id)
7.  blind_generators = BBS.create.create_generators(2,
                                                      "BLIND_" + api_id)
8.  generators.append(blind_generators)


9. proof = CoreProofGenWithPseudonym(PK,
                                     signature,
                                     Pseudonym,
                                     verifier_id,
                                     generators,
                                     header,
                                     ph,
                                     message_scalars,
                                     adj_disclosed_indexes,
                                     api_id)

10. if proof is INVALID, return INVALID
11. return proof
</artwork>
</section>
</section>

<section anchor="proof-verification-with-pseudonym"><name>Proof Verification with Pseudonym</name>
<t>This operation validates a BBS proof with a pseudonym, given the Signer's public key (PK), the proof, the pseudonym and the Verifier's identifier that was used to create it, a header and presentation header, the disclosed messages and lastly, the indexes those messages had in the original vector of signed messages. Validating the proof also validates the correctness and ownership by the Prover of the received pseudonym.</t>
<t>This operation makes use of <tt>CoreProofVerifyWithPseudonym</tt> as defined in <xref target="core-proof-verification"/>.</t>

<artwork>result = ProofVerifyWithPseudonym(PK,
                                  proof,
                                  L,
                                  Pseudonym,
                                  verifier_id,
                                  header,
                                  ph,
                                  disclosed_indexes,
                                  disclosed_messages)

Inputs:

- PK (REQUIRED), an octet string of the form outputted by the SkToPk
                 operation.
- proof (REQUIRED), an octet string of the form outputted by the
                    ProofGen operation.
- L (REQUIRED), the total number of signer provided messages in the
                original signature.
- Pseudonym (REQUIRED), A point of G1, different from the Identity of
                        G1, as outputted by the CalculatePseudonym
                        operation.
- verifier_id (REQUIRED), an octet string, representing the unique proof
                          Verifier identifier.
- header (OPTIONAL), an optional octet string containing context and
                     application specific information. If not supplied,
                     it defaults to an empty string.
- ph (OPTIONAL), an octet string containing the presentation header. If
                 not supplied, it defaults to an empty string.
- disclosed_messages (OPTIONAL), a vector of octet strings. If not
                                 supplied, it defaults to the empty
                                 array "()".
- disclosed_indexes (OPTIONAL), vector of unsigned integers in ascending
                                order. Indexes of disclosed messages. If
                                not supplied, it defaults to the empty
                                array "()".

Parameters:

- api_id, the octet string ciphersuite_id || "H2G_HM2S_PSEUDONYM_",
          where ciphersuite_id is defined by the ciphersuite and
          "H2G_HM2S_PSEUDONYM_" is an ASCII string comprised of
          9 bytes.
- (octet_point_length, octet_scalar_length), defined by the ciphersuite.

Outputs:

- result, either VALID or INVALID.

Deserialization:

1. proof_len_floor = 3 * octet_point_length + 4 * octet_scalar_length
2. if length(proof) &lt; proof_len_floor, return INVALID
3. U = floor((length(proof) - proof_len_floor) / octet_scalar_length)
4. total_no_messages = length(disclosed_indexes) +
     length(disclosed_committed_indexes) + U - 1
5. M = total_no_messages - L
6. R = length(disclosed_indexes)

Procedure:

1. message_scalars = messages_to_scalars(disclosed_messages, api_id)
2. generators = BBS.create_generators(L + 1, api_id)
3. blind_generators = []
4. if M &gt; -1, bind_generators = BBS.create_generators(M + 1,
                                                      "BLIND_" + api_id)
5. generators.append(blind_generators)
3. result = CoreProofVerifyWithPseudonym(PK,
                                         proof,
                                         Pseudonym,
                                         verifier_id,
                                         generators,
                                         header,
                                         ph,
                                         message_scalars,
                                         disclosed_indexes,
                                         api_id)
4. return result
</artwork>
</section>

<section anchor="core-operations"><name>Core Operations</name>

<section anchor="core-proof-generation"><name>Core Proof Generation</name>
<t>This operations computes a BBS proof and a zero-knowledge proof of correctness of the pseudonym in "parallel" (meaning using common randomness), as to both create a proof that the pseudonym was correctly calculated using an undisclosed value that the Prover knows (i.e., the <tt>pid</tt> value), but also that this value is "signed" by the BBS signature (the last undisclosed message). As a result, validating the proof guarantees that the pseudonym is correctly computed and that it was computed using the Prover identifier that was included in the BBS signature.</t>
<t>The operation uses the <tt>ProofInit</tt> and <tt>ProofFinalize</tt> operations defined in <xref target="I-D.irtf-cfrg-bbs-signatures"/> and the <tt>ProofWithPseudonymChallengeCalculate</tt> defined in <xref target="challenge-calculation"/>.</t>

<artwork>proof = CoreProofGenWithPseudonym(PK,
                                  signature,
                                  Pseudonym,
                                  verifier_id,
                                  generators,
                                  header,
                                  ph,
                                  messages,
                                  disclosed_indexes,
                                  api_id)

Inputs:

- PK (REQUIRED), an octet string of the form outputted by the SkToPk
                 operation.
- signature (REQUIRED), an octet string of the form outputted by the
                        Sign operation.
- Pseudonym (REQUIRED), A point of G1, different from the Identity of
                        G1, as outputted by the CalculatePseudonym
                        operation.
- verifier_id (REQUIRED), an octet string, representing the unique proof
                          Verifier identifier.
- generators (REQUIRED), vector of points in G1.
- header (OPTIONAL), an octet string containing context and application
                     specific information. If not supplied, it defaults
                     to an empty string.
- ph (OPTIONAL), an octet string containing the presentation header. If
                 not supplied, it defaults to an empty string.
- message_scalars (OPTIONAL), a vector of scalars representing the
                              messages. If not supplied, it defaults to
                              the empty array "()" must include the pid
                              scalar as last element.
- disclosed_indexes (OPTIONAL), vector of unsigned integers in ascending
                                order. Indexes of disclosed messages. If
                                not supplied, it defaults to the empty
                                array "()".
- api_id (OPTIONAL), an octet string. If not supplied it defaults to the
                     empty octet string ("").

Parameters:

- P1, fixed point of G1, defined by the ciphersuite.

Outputs:

- proof, an octet string; or INVALID.

Deserialization:

1.  signature_result = octets_to_signature(signature)
2.  if signature_result is INVALID, return INVALID
3.  (A, e) = signature_result
4.  L = length(message_scalars)
5.  R = length(disclosed_indexes)
6.  (i1, ..., iR) = disclosed_indexes
7.  if R &gt; L - 1, return INVALID, Note: we never reveal the pid value.
8.  U = L - R

// Note: pid is last message and is not revealed.
9.  undisclosed_indexes = (0, 1, ..., L - 1) \ disclosed_indexes
10. (i1, ..., iR) = disclosed_indexes
11. (j1, ..., jU) = undisclosed_indexes
12. disclosed_messages = (message_scalars[i1], ..., message_scalars[iR])
13. undisclosed_messages = (message_scalars[j1], ...,
                                                    message_scalars[jU])

ABORT if:

1. for i in disclosed_indexes, i &lt; 0 or i &gt; L - 1, // Note: pid  is L
                                                   // message and not
                                                   // revealed.

Procedure:

1.  random_scalars = calculate_random_scalars(5+U)
2.  init_res = ProofInit(PK,
                         signature_res,
                         header,
                         random_scalars,
                         generators,
                         message_scalars,
                         undisclosed_indexes,
                         api_id)
3.  if init_res is INVALID, return INVALID

4.  OP = hash_to_curve_g1(verifier_id, api_id)
5.  pid~ = random_scalars[5+U] // last element of random_scalars
6.  Ut = OP * pid~
7.  pseudonym_init_res = (Pseudonym, OP, Ut)

8.  challenge = ProofWithPseudonymChallengeCalculate(init_res,
                                                     pseudonym_init_res,
                                                     disclosed_indexes,
                                                     disclosed_messages,
                                                     ph,
                                                     api_id)
9.  proof = ProofFinalize(init_res, challenge, e_value, random_scalars,
                                                   undisclosed_messages)
10. return proof
</artwork>
</section>

<section anchor="core-proof-verification"><name>Core Proof Verification</name>
<t>This operation validates a BBS proof that also includes a pseudonym. Validating the proof, other than the correctness and integrity of the revealed messages, the header and the presentation header values, also guarantees that the supplied pseudonym was correctly calculated, i.e., that it was produced using the Verifier's identifier and the signed (but undisclosed) Prover's identifier, following the <tt>CalculatePseudonym</tt> operation defined in <xref target="calculate-pseudonym"/>.</t>
<t>The operation uses the <tt>ProofVerifyInit</tt> operation defined in <xref target="I-D.irtf-cfrg-bbs-signatures"/> and the <tt>ProofWithPseudonymChallengeCalculate</tt> defined in <xref target="challenge-calculation"/>.</t>

<artwork>result = CoreProofVerifyWithPseudonym(PK,
                                      proof,
                                      Pseudonym,
                                      verifier_id,
                                      generators,
                                      header,
                                      ph,
                                      disclosed_messages,
                                      disclosed_indexes,
                                      api_id)

Inputs:

- PK (REQUIRED), an octet string of the form outputted by the SkToPk
                 operation.
- proof (REQUIRED), an octet string of the form outputted by the
                    ProofGen operation.
- Pseudonym (REQUIRED), A point of G1, different from the Identity of
                        G1, as outputted by the CalculatePseudonym
                        operation.
- verifier_id (REQUIRED), an octet string, representing the unique proof
                          Verifier identifier.
- generators (REQUIRED), vector of points in G1.
- header (OPTIONAL), an optional octet string containing context and
                     application specific information. If not supplied,
                     it defaults to an empty string.
- ph (OPTIONAL), an octet string containing the presentation header. If
                 not supplied, it defaults to an empty string.
- disclosed_messages (OPTIONAL), a vector of scalars representing the
                                 messages. If not supplied, it defaults
                                 to the empty array "()".
- disclosed_indexes (OPTIONAL), vector of unsigned integers in ascending
                                order. Indexes of disclosed messages. If
                                not supplied, it defaults to the empty
                                array "()".
- api_id (OPTIONAL), an octet string. If not supplied it defaults to the
                     empty octet string ("").

Parameters:

- P1, fixed point of G1, defined by the ciphersuite.

Outputs:

- result, either VALID or INVALID.

Deserialization:

1. proof_result = octets_to_proof(proof)
2. if proof_result is INVALID, return INVALID
3. (Abar, Bbar, r2^, r3^, commitments, cp) = proof_result
4. W = octets_to_pubkey(PK)
5. if W is INVALID, return INVALID
6. R = length(disclosed_indexes)
7. (i1, ..., iR) = disclosed_indexes

ABORT if:

1. for i in disclosed_indexes, i &lt; 1 or i &gt; R + length(commitments) - 1

Procedure:

1.  init_res = ProofVerifyInit(PK, proof_result, header, generators,
                                    messages, disclosed_indexes, api_id)

2.  OP = hash_to_curve_g1(verifier_id)
3.  U = length(commitments)
4.  pid^ = commitments[U] // last element of the commitments
5.  Uv = OP * pid^ - Pseudonym * cp
6.  pseudonym_init_res = (Pseudonym, OP, Uv)

7.  challenge = ProofWithPseudonymChallengeCalculate(init_res,
                                                     pseudonym_init_res,
                                                     disclosed_indexes,
                                                     messages,
                                                     ph,
                                                     api_id)
8.  if cp != challenge, return INVALID
9.  if e(Abar, W) * e(Bbar, -BP2) != Identity_GT, return INVALID
10. return VALID
</artwork>
</section>
</section>

<section anchor="utility-operations"><name>Utility Operations</name>

<section anchor="challenge-calculation"><name>Challenge Calculation</name>

<artwork>challenge = ProofWithPseudonymChallengeCalculate(init_res,
                                                 pseudonym_init_res,
                                                 i_array,
                                                 msg_array,
                                                 ph, api_id)

Inputs:
- init_res (REQUIRED), vector representing the value returned after
                       initializing the proof generation or verification
                       operations, consisting of 5 points of G1 and a
                       scalar value, in that order.
- pseudonym_init_res (REQUIRED), vector representing the value returned
                                 after initializing the pseudonym proof,
                                 consisting of 3 points of G1.
- i_array (REQUIRED), array of non-negative integers (the indexes of
                      the disclosed messages).
- msg_array (REQUIRED), array of scalars (the disclosed messages after
                        mapped to scalars).
- ph (OPTIONAL), an octet string. If not supplied, it must default to
                 the empty octet string ("").
- api_id (OPTIONAL), an octet string. If not supplied it defaults to the
                     empty octet string ("").

Outputs:

- challenge, a scalar.

Definitions:

1. challenge_dst, an octet string representing the domain separation
                  tag: api_id || "H2S_" where "H2S_" is an ASCII string
                  comprised of 4 bytes.

Deserialization:

1. R = length(i_array)
2. (i1, ..., iR) = i_array
3. (msg_i1, ..., msg_iR) = msg_array
4. (Abar, Bbar, D, T1, T2, domain) = init_res
5. (Pseudonym, OP, Ut) = pseudonym_init_res

ABORT if:

1. R &gt; 2^64 - 1 or R != length(msg_array)
2. length(ph) &gt; 2^64 - 1

Procedure:
1. c_arr = (R, i1, msg_i1, i2, msg_i2, ..., iR, msg_iR, Abar, Bbar,
                                   D, T1, T2, Pseudonym, OP, Ut, domain)
2. c_octs = serialize(c_arr) || I2OSP(length(ph), 8) || ph
3. return hash_to_scalar(c_octs, challenge_dst)
</artwork>
</section>
</section>

<section anchor="security-considerations"><name>Security Considerations</name>
<t>TODO Security</t>
</section>

<section anchor="ciphersuites"><name>Ciphersuites</name>
<t>This document does not define new BBS ciphersuites. Its ciphersuite defined in <eref target="https://www.ietf.org/archive/id/draft-irtf-cfrg-bbs-signatures-03.html#name-ciphersuites">Section 6</eref> of <xref target="I-D.irtf-cfrg-bbs-signatures"/>) can be used to instantiate the operations of the described scheme.</t>
</section>

<section anchor="iana-considerations"><name>IANA Considerations</name>
<t>This document has no IANA actions.</t>
</section>

</middle>

<back>
<references><name>Normative References</name>
<xi:include href="https://xml2rfc.ietf.org/public/rfc/bibxml-ids/reference.I-D.irtf-cfrg-bbs-signatures.xml"/>
<xi:include href="https://xml2rfc.ietf.org/public/rfc/bibxml-ids/reference.I-D.irtf-cfrg-hash-to-curve.xml"/>
<xi:include href="https://xml2rfc.ietf.org/public/rfc/bibxml-ids/reference.I-D.irtf-cfrg-pairing-friendly-curves.xml"/>
<xi:include href="https://xml2rfc.ietf.org/public/rfc/bibxml-ids/reference.I-D.kalos-bbs-blind-signatures.xml"/>
<xi:include href="https://xml2rfc.ietf.org/public/rfc/bibxml/reference.RFC.2119.xml"/>
<xi:include href="https://xml2rfc.ietf.org/public/rfc/bibxml/reference.RFC.8174.xml"/>
</references>
<references><name>Informative References</name>
<reference anchor="BBS04" target="https://link.springer.com/chapter/10.1007/978-3-540-28628-8_3">
  <front>
    <title>Short Group Signatures</title>
    <author fullname="Dan Boneh" initials="D." surname="Boneh"/>
    <author fullname="Xavier Boyen" initials="X." surname="Boyen"/>
    <author fullname="Hovav Scacham" initials="H." surname="Shacham"/>
    <date year="2004"/>
  </front>
  <seriesInfo name="In" value="Advances in Cryptology"/>
  <seriesInfo name="pages" value="41-55"/>
</reference>
<reference anchor="DRBG" target="https://nvlpubs.nist.gov/nistpubs/SpecialPublications/NIST.SP.800-90Ar1.pdf">
  <front>
    <title>Recommendation for Random Number Generation Using Deterministic Random Bit Generators</title>
    <author>
      <organization>NIST</organization>
    </author>
  </front>
</reference>
</references>

<section anchor="acknowledgments"><name>Acknowledgments</name>
<t>TODO acknowledge.</t>
</section>

</back>

</rfc>
