Oblivious Pseudorandom Functions (OPRFs) using Prime-Order Groups
alex.davidson92@gmail.com
Cloudflare
101 Townsend St
San Francisco
United States of America
armfazh@cloudflare.com
Cloudflare
101 Townsend St
San Francisco
United States of America
nick@cloudflare.com
Cloudflare
101 Townsend St
San Francisco
United States of America
caw@heapingbits.net
Internet-Draft
An Oblivious Pseudorandom Function (OPRF) is a two-party protocol for
computing the output of a PRF. One party (the server) holds the PRF
secret key, and the other (the client) holds the PRF input. The
'obliviousness' property ensures that the server does not learn anything
about the client's input during the evaluation. The client should also
not learn anything about the server's secret PRF key. Optionally, OPRFs
can also satisfy a notion 'verifiability' (VOPRF). In this setting, the
client can verify that the server's output is indeed the result of
evaluating the underlying PRF with just a public key. This document
specifies OPRF and VOPRF constructions instantiated within prime-order
groups, including elliptic curves.
Discussion Venues
Source for this draft and an issue tracker can be found at
.
Introduction
A pseudorandom function (PRF) F(k, x) is an efficiently computable
function taking a private key k and a value x as input. This function is
pseudorandom if the keyed function K(_) = F(K, _) is indistinguishable
from a randomly sampled function acting on the same domain and range as
K(). An oblivious PRF (OPRF) is a two-party protocol between a server
and a client, where the server holds a PRF key k and the client holds
some input x. The protocol allows both parties to cooperate in computing
F(k, x) such that: the client learns F(k, x) without learning anything
about k; and the server does not learn anything about x or F(k, x).
A Verifiable OPRF (VOPRF) is an OPRF wherein the server can prove to the
client that F(k, x) was computed using the key k.
The usage of OPRFs has been demonstrated in constructing a number of
applications: password-protected secret sharing schemes ;
privacy-preserving password stores ; and
password-authenticated key exchange or PAKE . A VOPRF is
necessary in some applications, e.g., the Privacy Pass protocol
, wherein this VOPRF is used to generate
one-time authentication tokens to bypass CAPTCHA challenges. VOPRFs have
also been used for password-protected secret sharing schemes e.g.
.
This document introduces an OPRF protocol built in prime-order groups,
applying to finite fields of prime-order and also elliptic curve (EC)
groups. The protocol has the option of being extended to a VOPRF with
the addition of a NIZK proof for proving discrete log equality
relations. This proof demonstrates correctness of the computation, using
a known public key that serves as a commitment to the server's secret
key. The document describes the protocol, the public-facing API, and its
security properties.
Change log
draft-06:
- Specify of group element and scalar serialization.
- Remove info parameter from the protocol API and update domain separation guidance.
- Fold Unblind function into Finalize.
- Optimize ComputeComposites for servers (using knowledge of the private key).
- Specify deterministic key generation method.
- Update test vectors.
- Apply various editorial changes.
draft-05:
- Move to ristretto255 and decaf448 ciphersuites.
- Clean up ciphersuite definitions.
- Pin domain separation tag construction to draft version.
- Move key generation outside of context construction functions.
- Editorial changes.
draft-04:
- Introduce Client and Server contexts for controlling verifiability and
required functionality.
- Condense API.
- Remove batching from standard functionality (included as an extension)
- Add Curve25519 and P-256 ciphersuites for applications that prevent
strong-DH oracle attacks.
- Provide explicit prime-order group API and instantiation advice for
each ciphersuite.
- Proof-of-concept implementation in sage.
- Remove privacy considerations advice as this depends on applications.
draft-03:
- Certify public key during VerifiableFinalize.
- Remove protocol integration advice.
- Add text discussing how to perform domain separation.
- Drop OPRF_/VOPRF_ prefix from algorithm names.
- Make prime-order group assumption explicit.
- Changes to algorithms accepting batched inputs.
- Changes to construction of batched DLEQ proofs.
- Updated ciphersuites to be consistent with hash-to-curve and added
OPRF specific ciphersuites.
draft-02:
- Added section discussing cryptographic security and static DH oracles.
- Updated batched proof algorithms.
draft-01:
- Updated ciphersuites to be in line with
https://tools.ietf.org/html/draft-irtf-cfrg-hash-to-curve-04.
- Made some necessary modular reductions more explicit.
Terminology
The following terms are used throughout this document.
- PRF: Pseudorandom Function.
- OPRF: Oblivious Pseudorandom Function.
- VOPRF: Verifiable Oblivious Pseudorandom Function.
- Client: Protocol initiator. Learns pseudorandom function evaluation as
the output of the protocol.
- Server: Computes the pseudorandom function over a secret key. Learns
nothing about the client's input.
- NIZK: Non-interactive zero knowledge.
- DLEQ: Discrete Logarithm Equality.
Requirements
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 when, and only when, they
appear in all capitals, as shown here.
Preliminaries
The (V)OPRF protocol in this document has two primary dependencies:
-
GG: A prime-order group implementing the API described below in ,
with base point defined in the corresponding reference for each group.
(See for these base points.)
-
Hash: A cryptographic hash function that is indifferentiable from a
Random Oracle, whose output length is Nh bytes long.
specifies ciphersuites as combinations of GG and Hash.
Prime-Order Group Dependency
In this document, we assume the construction of an additive, prime-order
group GG for performing all mathematical operations. Such groups are
uniquely determined by the choice of the prime p that defines the
order of the group. We use GF(p) to represent the finite field of
order p. For the purpose of understanding and implementing this
document, we take GF(p) to be equal to the set of integers defined by
{0, 1, ..., p-1}.
The fundamental group operation is addition + with identity element
I. For any elements A and B of the group GG, A + B = B + A is
also a member of GG. Also, for any A in GG, there exists an element
-A such that A + (-A) = (-A) + A = I. Scalar multiplication is
equivalent to the repeated application of the group operation on an
element A with itself r-1 times, this is denoted as r*A = A + ... + A.
For any element A, p*A=I. Scalar base multiplication is equivalent to
the repeated application of the group operation on the base point with
itself r-1 times, this is denoted as ScalarBaseMult(r). The set of
scalars corresponds to GF(p).
We now detail a number of member functions that can be invoked on a
prime-order group GG.
- Order(): Outputs the order of GG (i.e. p).
- Identity(): Outputs the identity element of the group (i.e. I).
- HashToGroup(x): A member function of GG that deterministically maps
an array of bytes x to an element of GG. The map must ensure that,
for any adversary receiving R = HashToGroup(x), it is
computationally difficult to reverse the mapping. Examples of hash to
group functions satisfying this property are described for prime-order
(sub)groups of elliptic curves, see .
- HashToScalar(x): A member function of GG that deterministically maps
an array of bytes x to an element in GF(p). A recommended method
for its implementation is instantiating the hash to field function,
defined in setting the target field to GF(p).
- RandomScalar(): A member function of GG that chooses at random a
non-zero element in GF(p).
- SerializeElement(A): A member function of GG that maps a group element A
to a unique byte array buf of fixed length Ne.
- DeserializeElement(buf): A member function of GG that maps a byte array
buf to a group element A, or fails if the input is not a valid
byte representation of an element.
- SerializeScalar(s): A member function of GG that maps a scalar element s
to a unique byte array buf of fixed length Ns.
- DeserializeScalar(buf): A member function of GG that maps a byte array
buf to a scalar s, or fails if the input is not a valid byte
representation of a scalar.
Using the API of a prime-order group, we assume the existence of a function
GenerateKeyPair() that generates a random private and public key pair
(skS, pkS). One possible implementation might be to compute
skS = RandomScalar() and pkS = ScalarBaseMult(skS). We also assume the
existence of a DeriveKeyPair(seed) function that deterministically generates
a private and public key pair from input seed, where seed is a random
byte string that SHOULD have at least Ns bytes of entropy.
DeriveKeyPair(seed) computes skS = HashToScalar(seed) and
pkS = ScalarBaseMult(skS).
It is convenient in cryptographic applications to instantiate such
prime-order groups using elliptic curves, such as those detailed in
. For some choices of elliptic curves (e.g. those detailed in
, which require accounting for cofactors) there are some
implementation issues that introduce inherent discrepancies between
standard prime-order groups and the elliptic curve instantiation. In
this document, all algorithms that we detail assume that the group is a
prime-order group, and this MUST be upheld by any implementer. That is,
any curve instantiation should be written such that any discrepancies
with a prime-order group instantiation are removed. See
for advice corresponding to the implementation of this interface for
specific definitions of elliptic curves.
Other Conventions
- For any object x, we write len(x) to denote its length in bytes.
- For two byte arrays x and y, write x || y to denote their
concatenation.
- I2OSP and OS2IP: Convert a byte array to and from a non-negative
integer as described in . Note that these functions
operate on byte arrays in big-endian byte order.
All algorithm descriptions are written in a Python-like pseudocode. We
use the ABORT() function for presentational clarity to denote the
process of terminating the algorithm or returning an error accordingly.
We also use the CT_EQUAL(a, b) function to represent constant-time
byte-wise equality between byte arrays a and b. This function
returns true if a and b are equal, and false otherwise.
OPRF Protocol
In this section, we define two OPRF variants: a base mode and verifiable
mode. In the base mode, a client and server interact to compute y =
F(skS, x), where x is the client's input, skS is the server's private
key, and y is the OPRF output. The client learns y and the server learns
nothing. In the verifiable mode, the client also gets proof that the
server used skS in computing the function.
To achieve verifiability, as in the original work of , we
provide a zero-knowledge proof that the key provided as input by the
server in the Evaluate function is the same key as it used to produce
their public key. As an example of the nature of attacks that this
prevents, this ensures that the server uses the same private key for
computing the VOPRF output and does not attempt to "tag" individual
servers with select keys. This proof must not reveal the server's
long-term private key to the client.
The following one-byte values distinguish between these two modes:
Mode |
Value |
modeBase |
0x00 |
modeVerifiable |
0x01 |
Overview
Both participants agree on the mode and a choice of ciphersuite that is
used before the protocol exchange. Once established, the core protocol
runs to compute output = F(skS, input) as follows:
evaluatedElement, proof = Evaluate(skS, pkS, blindedElement)
evaluatedElement, proof
<----------
output = Finalize(input, blind, evaluatedElement, blindedElement, pkS, proof)
]]>
In Blind the client generates a token and blinding data. The server
computes the (V)OPRF evaluation in Evaluation over the client's
blinded token. In Finalize the client unblinds the server response,
verifies the server's proof if verifiability is required, and produces
a byte array corresponding to the output of the OPRF protocol.
Context Setup
Both modes of the OPRF involve an offline setup phase. In this phase,
both the client and server create a context used for executing the
online phase of the protocol. Prior to this phase, the key pair
(skS, pkS) should be generated by calling GenerateKeyPair()
or DeriveKeyPair() appropriately.
The base mode setup functions for creating client and server contexts are below:
The verifiable mode setup functions for creating client and server
contexts are below:
Each setup function takes a ciphersuite from the list defined in
. Each ciphersuite has a two-byte field ID used to
identify the suite.
Data Types
The following is a list of data structures that are defined for
providing inputs and outputs for each of the context interfaces defined
in . Data structure description uses TLS notation (see ,
Section 3).
This document uses the types Element and Scalar to denote elements of the
group GG and its underlying scalar field GF(p), respectively. For notational
clarity, PublicKey is an item of type Element and PrivateKey is an item
of type Scalar. SerializedElement and SerializedScalar are serialized
representations of Element and Scalar types of length Ne and Ns,
respectively; see . ClientInput is an opaque byte string of arbitrary
length. Proof is a sequence of two SerializedScalar elements, as shown below.
Context APIs
In this section, we detail the APIs available on the client and server
(V)OPRF contexts.
Server Context
The ServerContext encapsulates the context string constructed during
setup and the (V)OPRF key pair. It has three functions, Evaluate,
FullEvaluate and VerifyFinalize described below. Evaluate takes
serialized representations of blinded group elements from the client as inputs.
FullEvaluate takes ClientInput values, and it is useful for applications
that need to compute the whole OPRF protocol on the server side only.
VerifyFinalize takes ClientInput values and their corresponding output
digests from Finalize as input, and returns true if the inputs match the outputs.
Note that VerifyFinalize and FullEvaluate are not used in the main OPRF
protocol. They are exposed as an API for building higher-level protocols.
FullEvaluate
[[RFC editor: please change "VOPRF06" to "RFCXXXX", where XXXX is the final number, here and elsewhere before publication.]]
VerifyFinalize
[[RFC editor: please change "VOPRF06" to "RFCXXXX", where XXXX is the final number, here and elsewhere before publication.]]
VerifiableServerContext
The VerifiableServerContext extends the base ServerContext with an
augmented Evaluate() function. This function produces a proof that
skS was used in computing the result. It makes use of the helper
functions GenerateProof and ComputeComposites, described below.
Evaluate
The helper functions GenerateProof and ComputeComposites are defined
below.
GenerateProof
Batching inputs
Unlike other functions, ComputeComposites takes lists of inputs,
rather than a single input. Applications can take advantage of this
functionality by invoking GenerateProof on batches of inputs to
produce a combined, constant-size proof. (In the pseudocode above,
the single inputs blindedElement and evaluatedElement are passed as
one-item lists to ComputeComposites.)
In particular, servers can produce a single, constant-sized proof for N
client inputs sent in a single request, rather than one proof per client
input. This optimization benefits clients and servers since it amortizes
the cost of proof generation and bandwidth across multiple requests.
Fresh Randomness
We note here that it is essential that a different r value is used for
every invocation. If this is not done, then this may leak skS as is
possible in Schnorr or (EC)DSA scenarios where fresh randomness is not
used.
ComputeComposites
The definition of ComputeComposites is given below. This function is
used both on generation and verification of the proof.
If the private key is known, as is the case for the server, this function
can be optimized as shown in ComputeCompositesFast below.
Client Context
The ClientContext encapsulates the context string constructed during
setup. It has two functions, Blind() and Finalize(), as described
below. It also has an internal function, Unblind(), which is used
by Finalize. Its implementation varies depending on the mode.
Blind
We note here that the blinding mechanism that we use can be modified
slightly with the opportunity for making performance gains in some
scenarios. We detail these modifications in .
Unblind
In this mode, Unblind takes only two inputs. The additional inputs indicated
in are only omitted as they are ignored. These additional
inputs are only useful for the verifiable mode, described in .
Finalize
Finalize depends on the internal Unblind function. In this mode, Finalize
and does not include all inputs listed in . These additional
inputs are only useful for the verifiable mode, described in .
VerifiableClientContext
The VerifiableClientContext extends the base ClientContext with the
desired server public key pkS with an augmented Unblind() function.
This function verifies an evaluation proof using pkS. It makes use of
the helper function ComputeComposites described above. It has one
helper function, VerifyProof(), defined below.
VerifyProof
This algorithm outputs a boolean verified which indicates whether the
proof inside of the evaluation verifies correctly, or not.
Domain Separation
Applications SHOULD construct input to the protocol to provide domain
separation. Any system which has multiple (V)OPRF applications should
distinguish client inputs to ensure the OPRF results are separate.
Guidance for constructing info can be found in
; Section 3.1.
Ciphersuites
A ciphersuite (also referred to as 'suite' in this document) for the protocol
wraps the functionality required for the protocol to take place. This
ciphersuite should be available to both the client and server, and agreement
on the specific instantiation is assumed throughout. A ciphersuite contains
instantiations of the following functionalities:
-
GG: A prime-order group exposing the API detailed in , with base
point 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 , Section 3.1.
-
Hash: A cryptographic hash function that is indifferentiable from a
Random Oracle, whose output length is Nh bytes long.
This section specifies ciphersuites with supported groups and hash functions.
For each ciphersuite, contextString is that which is computed in the Setup
functions.
Applications should take caution in using ciphersuites targeting P-256
and ristretto255. See for related discussion.
OPRF(ristretto255, SHA-512)
-
Group: ristretto255
- HashToGroup(): Use hash_to_ristretto255
with DST =
"VOPRF06-HashToGroup-" || contextString, and expand_message = expand_message_xmd
using SHA-512.
- HashToScalar(): Compute uniform_bytes using expand_message = expand_message_xmd,
DST = "VOPRF06-HashToScalar-" || contextString, and output length 64, interpret
uniform_bytes as a 512-bit integer in little-endian order, and reduce the integer
modulo Order().
- Serialization: Both group elements and scalars are encoded in Ne = Ns = 32
bytes. For group elements, use the 'Encode' and 'Decode' functions from
. For scalars, ensure they are fully reduced modulo p and
in little-endian order.
- Hash: SHA-512, and Nh = 64.
- ID: 0x0001
OPRF(decaf448, SHA-512)
-
Group: decaf448
- HashToGroup(): Use hash_to_decaf448
with DST =
"VOPRF06-HashToGroup-" || contextString, and expand_message = expand_message_xmd
using SHA-512.
- HashToScalar(): Compute uniform_bytes using expand_message = expand_message_xmd,
DST = "VOPRF06-HashToScalar-" || contextString, and output length 64, interpret
uniform_bytes as a 512-bit integer in little-endian order, and reduce the integer
modulo Order().
- Serialization: Both group elements and scalars are encoded in Ne = Ns = 56
bytes. For group elements, use the 'Encode' and 'Decode' functions from
. For scalars, ensure they are fully reduced modulo p and
in little-endian order.
- Hash: SHA-512, and Nh = 64.
- ID: 0x0002
OPRF(P-256, SHA-256)
-
Group: P-256 (secp256r1)
- HashToGroup(): Use hash_to_curve with suite P256_XMD:SHA-256_SSWU_RO_
and DST =
"VOPRF06-HashToGroup-" || contextString.
- HashToScalar(): Use hash_to_field from
using Order() as the prime modulus, L = 48, expand_message_xmd
with SHA-256, and DST = "VOPRF06-HashToScalar-" || contextString.
- Serialization: Elements are serialized as Ne = 33 byte strings using
compressed point encoding for the curve . Scalars are serialized as
Ns = 32 byte strings by fully reducing the value modulo p and in big-endian
order.
- Hash: SHA-256, and Nh = 32.
- ID: 0x0003
OPRF(P-384, SHA-512)
-
Group: P-384 (secp384r1)
- HashToGroup(): Use hash_to_curve with suite P384_XMD:SHA-512_SSWU_RO_
and DST =
"VOPRF06-HashToGroup-" || contextString.
- HashToScalar(): Use hash_to_field from
using Order() as the prime modulus, L = 72, expand_message_xmd
with SHA-512, and DST = "VOPRF06-HashToScalar-" || contextString.
- Serialization: Elements are serialized as Ne = 49 byte strings using
compressed point encoding for the curve . Scalars are serialized as
Ns = 48 byte strings by fully reducing the value modulo p and in big-endian
order.
- Hash: SHA-512, and Nh = 64.
- ID: 0x0004
OPRF(P-521, SHA-512)
-
Group: P-521 (secp521r1)
- HashToGroup(): Use hash_to_curve with suite P521_XMD:SHA-512_SSWU_RO_
and DST =
"VOPRF06-HashToGroup-" || contextString.
- HashToScalar(): Use hash_to_field from
using Order() as the prime modulus, L = 98, expand_message_xmd
with SHA-512, and DST = "VOPRF06-HashToScalar-" || contextString.
- Serialization: Elements are serialized as Ne = 67 byte strings using
compressed point encoding for the curve . Scalars are serialized as
Ns = 66 byte strings by fully reducing the value modulo p and in big-endian
order.
- Hash: SHA-512, and Nh = 64.
- ID: 0x0005
Security Considerations
This section discusses the cryptographic security of our protocol, along
with some suggestions and trade-offs that arise from the implementation
of an OPRF.
Security Properties
The security properties of an OPRF protocol with functionality y = F(k,
x) include those of a standard PRF. Specifically:
- Pseudorandomness: F is pseudorandom if the output y = F(k,x) on any
input x is indistinguishable from uniformly sampling any element in
F's range, for a random sampling of k.
In other words, consider an adversary that picks inputs x from the
domain of F and evaluates F on (k,x) (without knowledge of randomly
sampled k). Then the output distribution F(k,x) is indistinguishable
from the output distribution of a randomly chosen function with the same
domain and range.
A consequence of showing that a function is pseudorandom, is that it is
necessarily non-malleable (i.e. we cannot compute a new evaluation of F
from an existing evaluation). A genuinely random function will be
non-malleable with high probability, and so a pseudorandom function must
be non-malleable to maintain indistinguishability.
An OPRF protocol must also satisfy the following property:
- Oblivious: The server must learn nothing about the client's input or
the output of the function. In addition, the client must learn nothing
about the server's private key.
Essentially, obliviousness tells us that, even if the server learns the
client's input x at some point in the future, then the server will not
be able to link any particular OPRF evaluation to x. This property is
also known as unlinkability .
Optionally, for any protocol that satisfies the above properties, there
is an additional security property:
- Verifiable: The client must only complete execution of the protocol if
it can successfully assert that the OPRF output it computes is
correct. This is taken with respect to the OPRF key held by the
server.
Any OPRF that satisfies the 'verifiable' security property is known as a
verifiable OPRF, or VOPRF for short. In practice, the notion of
verifiability requires that the server commits to the key before the
actual protocol execution takes place. Then the client verifies that the
server has used the key in the protocol using this commitment. In the
following, we may also refer to this commitment as a public key.
Cryptographic Security
Below, we discuss the cryptographic security of the (V)OPRF protocol
from , relative to the necessary cryptographic assumptions
that need to be made.
Computational Hardness Assumptions
Each assumption states that the problems specified below are
computationally difficult to solve in relation to a particular choice of
security parameter sp.
Let GG = GG(sp) be a group with prime-order p, and let GF(p) be a finite
field of order p.
Discrete-log (DL) Problem
Given G, a generator of GG, and H = hG for some h in GF(p); output h.
Decisional Diffie-Hellman (DDH) Problem
Sample uniformly at random d in {0,1}. Given (G, aG, bG, C), where
- G is a generator of GG;
- a,b are elements of GF(p);
- if d == 0: C = abG; else: C is sampled uniformly at random from GG.
Output d' == d.
Protocol Security
Our OPRF construction is based on the VOPRF construction known as
2HashDH-NIZK given by ; essentially without providing
zero-knowledge proofs that verify that the output is correct. Our VOPRF
construction is identical to the construction, except that we
can optionally perform multiple VOPRF evaluations in one go, whilst only
constructing one NIZK proof object. This is enabled using an established
batching technique.
Consequently, the cryptographic security of our construction is based on
the assumption that the One-More Gap DH is computationally difficult to
solve.
The (N,Q)-One-More Gap DH (OMDH) problem asks the following.
The original paper gives a security proof that the
2HashDH-NIZK construction satisfies the security guarantees of a VOPRF
protocol under the OMDH assumption in the universal
composability (UC) security model.
Q-Strong-DH Oracle
A side-effect of our OPRF design is that it allows instantiation of a
oracle for constructing Q-strong-DH (Q-sDH) samples. The Q-Strong-DH
problem asks the following.
The assumption that this problem is hard was first introduced in
. Since then, there have been a number of cryptanalytic studies
that have reduced the security of the assumption below that implied by
the group instantiation (for example, and ). In
summary, the attacks reduce the security of the group instantiation by
log_2(Q) bits.
As an example, suppose that a group instantiation is used that provides
128 bits of security against discrete log cryptanalysis. Then an
adversary with access to a Q-sDH oracle and makes Q=2^20 queries can
reduce the security of the instantiation by log_2(2^20) = 20 bits.
Notice that it is easy to instantiate a Q-sDH oracle using the OPRF
functionality that we provide. A client can just submit sequential
queries of the form (G, k * G, (k^2)G, ..., (k^(Q-1))G), where each
query is the output of the previous interaction. This means that any
client that submits Q queries to the OPRF can use the aforementioned
attacks to reduce the security of the group instantiation by log_2(Q) bits.
Recall that from a malicious client's perspective, the adversary wins if
they can distinguish the OPRF interaction from a protocol that computes
the ideal functionality provided by the PRF.
Implications for Ciphersuite Choices
The OPRF instantiations that we recommend in this document are informed
by the cryptanalytic discussion above. In particular, choosing elliptic
curves configurations that describe 128-bit group instantiations would
appear to in fact instantiate an OPRF with 128-log_2(Q) bits of
security.
In most cases, it would require an informed and persistent attacker to
launch a highly expensive attack to reduce security to anything much
below 100 bits of security. We see this possibility as something that
may result in problems in the future. For applications that cannot
tolerate discrete logarithm security of lower than 128 bits, we
recommend only implementing ciphersuites with IDs: 0x0002, 0x0004, and
0x0005.
Hashing to Group
A critical requirement of implementing the prime-order group using
elliptic curves is a method to instantiate the function
GG.HashToGroup, that maps inputs to group elements. In the elliptic
curve setting, this deterministically maps inputs x (as byte arrays) to
uniformly chosen points on the curve.
In the security proof of the construction Hash is modeled as a random
oracle. This implies that any instantiation of GG.HashToGroup must be
pre-image and collision resistant. In we give
instantiations of this functionality based on the functions described in
. Consequently, any OPRF implementation
must adhere to the implementation and security considerations discussed
in when instantiating the function.
Timing Leaks
To ensure no information is leaked during protocol execution, all
operations that use secret data MUST run in constant time. Operations that
SHOULD run in constant time include all prime-order group operations and
proof-specific operations (GenerateProof() and VerifyProof()).
Key Rotation
Since the server's key is critical to security, the longer it is exposed
by performing (V)OPRF operations on client inputs, the longer it is
possible that the key can be compromised. For example, if the key is kept
in circulation for a long period of time, then it also allows the
clients to make enough queries to launch more powerful variants of the
Q-sDH attacks from .
To combat attacks of this nature, regular key rotation should be
employed on the server-side. A suitable key-cycle for a key used to
compute (V)OPRF evaluations would be between one week and six months.
Additive Blinding
Let H refer to the function GG.HashToGroup, in we assume
that the client-side blinding is carried out directly on the output of
H(x), i.e. computing r * H(x) for some r sampled uniformly at random
from GF(p). In the document, it is noted that it
may be more efficient to use additive blinding (rather than multiplicative)
if the client can preprocess some values. For example, a valid way of
computing additive blinding would be to instead compute H(x) + (r * G),
where G is the fixed generator for the group GG.
The advantage of additive blinding is that it allows the client to
pre-process tables of blinded scalar multiplications for G. This may
give it a computational efficiency advantage (due to the fact that a
fixed-base multiplication can be calculated faster than a variable-base
multiplication). Pre-processing also reduces the amount of computation
that needs to be done in the online exchange. Choosing one of these
values r * G (where r is the scalar value that is used), then
computing H(x) + (r * G) is more efficient than computing r * H(x).
Therefore, it may be advantageous to define the OPRF and VOPRF protocols
using additive (rather than multiplicative) blinding. In fact,
the only algorithms that need to change are Blind and Unblind (and
similarly for the VOPRF variants).
We define the variants of the algorithms in for performing
additive blinding below, called AdditiveBlind and AdditiveUnblind,
along with a new algorithm Preprocess. The Preprocess algorithm can
take place offline and before the rest of the OPRF protocol. AdditiveBlind
takes the preprocessed values as inputs. AdditiveUnblind takes the
preprocessed values and evaluated element from the server as inputs.
AdditiveUnblind
Let P = GG.HashToGroup(input). Notice that AdditiveUnblind computes:
by the commutativity of the scalar field. This is the same
output as in the Unblind algorithm for multiplicative blinding.
Note that the verifiable variant of AdditiveUnblind works as above but
includes the step to VerifyProof, as specified in .
Parameter Commitments
For some applications, it may be desirable for the server to bind tokens to
certain parameters, e.g., protocol versions, ciphersuites, etc. To
accomplish this, the server should use a distinct scalar for each parameter
combination. Upon redemption of a token T from the client, the server can
later verify that T was generated using the scalar associated with the
corresponding parameters.
Acknowledgements
This document resulted from the work of the Privacy Pass team
. The authors would also like to acknowledge helpful
conversations with Hugo Krawczyk. Eli-Shaoul Khedouri provided
additional review and comments on key consistency. Daniel Bourdrez,
Tatiana Bradley, Sofia Celi, Frank Denis, and Bas Westerbaan also
provided helpful input and contributions to the document.
References
Normative References
Key words for use in RFCs to Indicate Requirement Levels
In many standards track documents several words are used to signify the requirements in the specification. These words are often capitalized. This document defines these words as they should be interpreted in IETF documents. This document specifies an Internet Best Current Practices for the Internet Community, and requests discussion and suggestions for improvements.
Elliptic Curves for Security
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.
Privacy Pass
Short Signatures Without Random Oracles
The Static Diffie-Hellman Problem
Security Analysis of the Strong Diffie-Hellman Problem
Highly-Efficient and Composable Password-Protected Secret Sharing (Or, How to Protect Your Bitcoin Wallet Online)
Round-Optimal Password-Protected Secret Sharing and T-PAKE in the Password-Only model
SPHINX, A Password Store that Perfectly Hides from Itself
Privacy Pass, Bypassing Internet Challenges Anonymously
SEC 1: Elliptic Curve Cryptography
SEC 2: Recommended Elliptic Curve Domain Parameters
Public Key Cryptography for the Financial Services Industry: the Elliptic Curve Digital Signature Algorithm (ECDSA)
ANSI
The OPAQUE Asymmetric PAKE Protocol
This document describes the OPAQUE protocol, a secure asymmetric password-authenticated key exchange (aPAKE) that supports mutual authentication in a client-server setting without reliance on PKI and with security against pre-computation attacks upon server compromise. In addition, the protocol provides forward secrecy and the ability to hide the password from the server, even during password registration. This document specifies the core OPAQUE protocol, along with several instantiations in different authenticated key exchange protocols.
Privacy Pass: The Protocol
This document specifies the Privacy Pass protocol. This protocol provides anonymity-preserving authorization of clients to servers. In particular, client re-authorization events cannot be linked to any previous initial authorization. Privacy Pass is intended to be used as a performant protocol in the application-layer.
Ambiguity of Uppercase vs Lowercase in RFC 2119 Key Words
RFC 2119 specifies common key words that may be used in protocol specifications. This document aims to reduce the ambiguity by clarifying that only UPPERCASE usage of the key words have the defined special meanings.
Hashing to Elliptic Curves
This document specifies a number of algorithms for encoding or hashing an arbitrary string to a point on an elliptic curve.
PKCS #1: RSA Cryptography Specifications Version 2.2
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.
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.
This document also obsoletes RFC 3447.
The ristretto255 and decaf448 Groups
This memo specifies two prime-order groups, ristretto255 and decaf448, suitable for safely implementing higher-level and complex cryptographic protocols. The ristretto255 group can be implemented using Curve25519, allowing existing Curve25519 implementations to be reused and extended to provide a prime-order group. Likewise, the decaf448 group can be implemented using edwards448.
Informative References
The Transport Layer Security (TLS) Protocol Version 1.3
This document specifies version 1.3 of the Transport Layer Security (TLS) protocol. TLS allows client/server applications to communicate over the Internet in a way that is designed to prevent eavesdropping, tampering, and message forgery.
This document updates RFCs 5705 and 6066, and obsoletes RFCs 5077, 5246, and 6961. This document also specifies new requirements for TLS 1.2 implementations.
Test Vectors
This section includes test vectors for the (V)OPRF protocol specified
in this document. For each ciphersuite specified in ,
there is a set of test vectors for the protocol when run in the base
mode and verifiable mode. Each test vector lists the batch size for
the evaluation. Each test vector value is encoded as a hexadecimal
byte string. The label for each test vector value is described below.
- "Input": The client input, an opaque byte string.
- "Blind": The blind value output by Blind(), a serialized Scalar
of Ns bytes long.
- "BlindedElement": The blinded value output by Blind(), a serialized
Element of Ne bytes long.
- "EvaluatedElement": The evaluated element output by Evaluate(),
a serialized Element of Ne bytes long.
- "EvaluationProofC": The "c" component of the Evaluation proof (only
listed for verifiable mode test vectors), a serialized Scalar of
Ns bytes long.
- "EvaluationProofS": The "s" component of the Evaluation proof (only
listed for verifiable mode test vectors), a serialized Scalar of
Ns bytes long.
- "Output": The OPRF output, a byte string of length Nh bytes.
Test vectors with batch size B > 1 have inputs separated by a comma
",". Applicable test vectors will have B different values for the
"Input", "Blind", "BlindedElement", "EvaluationElement", and
"Output" fields.
The server key material, pkSm and skSm, are listed under the mode for
each ciphersuite. Both pkSm and skSm are the serialized values of
pkS and skS, respectively, as used in the protocol. Each key pair
is derived from a seed, which is listed as well.
OPRF(ristretto255, SHA-512)
Base Mode
Test Vector 1, Batch Size 1
Test Vector 2, Batch Size 1
Verifiable Mode
Test Vector 1, Batch Size 1
Test Vector 2, Batch Size 1
Test Vector 3, Batch Size 2
OPRF(decaf448, SHA-512)
Base Mode
Test Vector 1, Batch Size 1
Test Vector 2, Batch Size 1
Verifiable Mode
Test Vector 1, Batch Size 1
Test Vector 2, Batch Size 1
Test Vector 3, Batch Size 2
OPRF(P-256, SHA-256)
Base Mode
Test Vector 1, Batch Size 1
Test Vector 2, Batch Size 1
Verifiable Mode
Test Vector 1, Batch Size 1
Test Vector 2, Batch Size 1
Test Vector 3, Batch Size 2
OPRF(P-384, SHA-512)
Base Mode
Test Vector 1, Batch Size 1
Test Vector 2, Batch Size 1
Verifiable Mode
Test Vector 1, Batch Size 1
Test Vector 2, Batch Size 1
Test Vector 3, Batch Size 2
OPRF(P-521, SHA-512)
Base Mode
Test Vector 1, Batch Size 1
Test Vector 2, Batch Size 1
Verifiable Mode
Test Vector 1, Batch Size 1
Test Vector 2, Batch Size 1
Test Vector 3, Batch Size 2