]>
Oblivious Pseudorandom Functions (OPRFs) Using Prime-Order GroupsBrave Softwarealex.davidson92@gmail.comCloudflare, Inc.101 Townsend StSan FranciscoCAUnited States of Americaarmfazh@cloudflare.comCloudflare, Inc.101 Townsend StSan FranciscoCAUnited States of Americanicholas.sullivan+ietf@gmail.comCloudflare, Inc.101 Townsend StSan FranciscoCAUnited States of Americacaw@heapingbits.netCrypto ForumAn Oblivious Pseudorandom Function (OPRF) is a two-party protocol between
a client and a server for computing the output of a Pseudorandom Function (PRF).
The server provides the PRF private key, and the client provides the PRF
input. At the end of the protocol, the client learns the PRF output without
learning anything about the PRF private key, and the server learns neither
the PRF input nor output. An OPRF can also satisfy a notion of 'verifiability',
called a VOPRF. A VOPRF ensures clients can verify that the server used a
specific private key during the execution of the protocol. A VOPRF can also
be partially oblivious, called a POPRF. A POPRF allows clients and servers
to provide public input to the PRF computation. This document specifies an OPRF,
VOPRF, and POPRF instantiated within standard prime-order groups, including
elliptic curves. This document is a product of the Crypto Forum Research Group
(CFRG) in the IRTF.IntroductionA 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, wherein 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 also proves
to the client that F(k, x) was produced by the key k corresponding
to the server's public key, which the client knows.
A Partially Oblivious PRF (POPRF)
is a variant of a VOPRF, where the client and server interact in computing
F(k, x, y), for some PRF F with server-provided key k, client-provided
input x, and public input y, and the client receives proof
that F(k, x, y) was computed using k corresponding to the public key
that the client knows. A POPRF with fixed input y is functionally
equivalent to a VOPRF.OPRFs have a variety of applications, including password-protected secret
sharing schemes , privacy-preserving password stores , and
password-authenticated key exchange (PAKE) .
Verifiable OPRFs are necessary in some applications, such as Privacy Pass
. Verifiable OPRFs have also been used for
password-protected secret sharing schemes, such as that of .This document specifies OPRF, VOPRF, and POPRF protocols built upon
prime-order groups. The document describes each protocol variant,
along with application considerations, and their security properties.This document represents the consensus of the Crypto Forum Research
Group (CFRG). It is not an IETF product and is not a standard.Requirements Language
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.
Notation and TerminologyThe following functions and notation are used throughout the document.
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(x, xLen) converts a nonnegative integer x into a byte array
of specified length xLen, as described in . Note that
this function returns a byte array in big-endian byte order.
The notation T U[N] refers to an array called U, containing N items of type
T. The type opaque means one single byte of uninterpreted data. Items of
the array are zero-indexed and referred to as U[j], such that 0 <= j < N.
All algorithms and procedures described in this document are laid out
in a Python-like pseudocode. Each function takes a set of inputs and parameters
and produces a set of output values. Parameters become constant values once the
protocol variant and the ciphersuite are fixed.The PrivateInput data type refers to inputs that are known only to the client
in the protocol, whereas the PublicInput data type refers to inputs that are
known to both the client and server in the protocol. Both PrivateInput and
PublicInput are opaque byte strings of arbitrary length no larger than 2^{16} - 1 bytes.
This length restriction exists because PublicInput and PrivateInput values
are length-prefixed with two bytes before use throughout the protocol.String values, such as "DeriveKeyPair", "Seed-", and "Finalize", are ASCII string literals.The following terms are used throughout this document.
PRF:
Pseudorandom Function
OPRF:
Oblivious Pseudorandom Function
VOPRF:
Verifiable Oblivious Pseudorandom Function
POPRF:
Partially Oblivious Pseudorandom Function
Client:
Protocol initiator. Learns PRF evaluation as
the output of the protocol.
Server:
Computes the PRF using a private key. Learns
nothing about the client's input or output.
PreliminariesThe protocols in this document have two primary dependencies:
Group:
A prime-order group implementing the API described below in .
See for specific instances of groups.
Hash:
A cryptographic hash function whose output length is Nh bytes.
specifies ciphersuites as combinations of Group and Hash.Prime-Order GroupIn this document, we assume the construction of an additive, prime-order
group, denoted Group, for performing all mathematical operations. In prime-order groups,
any element (other than the identity) can generate the other elements of the
group. Usually, one element
is fixed and defined as the group generator. Such groups are
uniquely determined by the choice of the prime p that defines the
order of the group. (However, different representations
of the group for a single p may exist. lists specific groups that
indicate both the order and representation.)The fundamental group operation is addition + with identity element
I. For any elements A and B of the group, A + B = B + A is
also a member of the group. Also, for any A in the group, there exists an element
-A, such that A + (-A) = (-A) + A = I. Scalar multiplication by
r 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. The case when the scalar multiplication is
performed on the group generator is denoted as ScalarMultGen(r).
Given two elements A and B, the discrete logarithm problem is to find
an integer k, such that B = k * A. Thus, k is the discrete logarithm of
B with respect to the base A.
The set of scalars corresponds to GF(p), a prime field of order p, and is
represented as the set of integers defined by {0, 1, ..., p - 1}.
This document uses types
Element and Scalar to denote elements of the group and its set of
scalars, respectively.We now detail a number of member functions that can be invoked on a
prime-order group.
Order():
Outputs the order of the group (i.e., p).
Identity():
Outputs the identity element of the group (i.e., I).
Generator():
Outputs the generator element of the group.
HashToGroup(x):
Deterministically maps
an array of bytes x to an element of Group. The map must ensure that,
for any adversary receiving R = HashToGroup(x), it is
computationally difficult to reverse the mapping. This function is optionally
parameterized by a domain separation tag (DST); see .
Security properties of this function are described
in .
HashToScalar(x):
Deterministically maps
an array of bytes x to an element in GF(p). This function is optionally
parameterized by a DST; see . Security properties of this
function are described in .
RandomScalar():
Chooses at random a nonzero element in GF(p).
ScalarInverse(s):
Returns the inverse of input Scalar s on GF(p).
SerializeElement(A):
Maps an Element A
to a canonical byte array buf of fixed-length Ne.
DeserializeElement(buf):
Attempts to map a byte array buf to
an Element A and fails if the input is not the valid canonical byte
representation of an element of the group. This function can raise a
DeserializeError if deserialization fails or A is the identity element of
the group; see for group-specific input validation steps.
SerializeScalar(s):
Maps Scalar s to a canonical
byte array buf of fixed-length Ns.
DeserializeScalar(buf):
Attempts to map a byte array buf to Scalar s.
This function can raise a DeserializeError if deserialization fails; see
for group-specific input validation steps.
contains details for the implementation of this interface
for different prime-order groups instantiated over elliptic curves. In
particular, for some choices of elliptic curves, e.g., those detailed in
, which require accounting for cofactors,
describes required steps necessary to ensure the resulting group is of
prime order.Discrete Logarithm Equivalence ProofsA proof of knowledge allows a prover to convince a verifier that some
statement is true. If the prover can generate a proof without interaction
with the verifier, the proof is noninteractive. If the verifier learns
nothing other than whether the statement claimed by the prover is true or
false, the proof is zero-knowledge.This section describes a noninteractive, zero-knowledge proof for discrete
logarithm equivalence (DLEQ), which is used in the construction of VOPRF and
POPRF. A DLEQ proof demonstrates that two pairs of
group elements have the same discrete logarithm without revealing the
discrete logarithm.The DLEQ proof resembles the Chaum-Pedersen proof, which
is shown to be zero-knowledge by Jarecki, et al. and is
noninteractive after applying the Fiat-Shamir transform .
Furthermore, Davidson, et al. showed a proof system for
batching DLEQ proofs that has constant-size proofs with respect to the
number of inputs.
The specific DLEQ proof system presented below follows this latter
construction with two modifications: (1) the transcript used to generate
the seed includes more context information and (2) the individual challenges
for each element in the proof is derived from a seed-prefixed hash-to-scalar
invocation, rather than being sampled from a seeded Pseudorandom Number Generator (PRNG).
The description is split into
two subsections: one for generating the proof, which is done by servers
in the verifiable protocols, and another for verifying the proof, which is
done by clients in the protocol.Proof GenerationGenerating a proof is done with the GenerateProof function, as defined below.
Given Element values A and B, two non-empty lists of Element values C and D of length
m, and Scalar k, this function produces a proof that k * A == B
and k * C[i] == D[i] for each i in [0, ..., m - 1].
The output is a value of type Proof, which is a tuple of two Scalar
values. We use the notation proof[0] and proof[1] to denote
the first and second elements in this tuple, respectively.GenerateProof accepts lists of inputs to amortize the cost of proof
generation. Applications can take advantage of this functionality to
produce a single, constant-sized proof for m DLEQ inputs, rather
than m proofs for m DLEQ inputs.The helper function ComputeCompositesFast is as defined below and is an
optimization of the ComputeComposites function for servers since they have
knowledge of the private key.When used in the protocol described in , the parameter contextString is
as defined in .Proof VerificationVerifying a proof is done with the VerifyProof function, as defined below.
This function takes Element values A and B, two non-empty lists of Element values C and D
of length m, and a Proof value output from GenerateProof. It outputs a
single boolean value indicating whether or not the proof is valid for the
given DLEQ inputs. Note this function can verify proofs on lists of inputs
whenever the proof was generated as a batched DLEQ proof with the same inputs.The definition of ComputeComposites is given below.When used in the protocol described in , the parameter contextString is
as defined in .ProtocolIn this section, we define and describe three protocol variants referred to as the
OPRF, VOPRF, and POPRF modes. Each of these variants involves two messages between the
client and server, but they differ slightly in terms of the security properties; see
for more information. A high-level description of the functionality
of each mode follows.In the OPRF mode, a client and server interact to compute output = F(skS, input),
where input is the client's private input, skS is the server's private key,
and output is the OPRF output. After the execution of the protocol, the
client learns the output and the server learns nothing.
This interaction is shown below.In the VOPRF mode, the client additionally receives proof that the server used
skS in computing the function. To achieve verifiability, as in , the
server provides a zero-knowledge proof that the key provided as input by the server in
the BlindEvaluate function is the same key as is used to produce the server's public key, pkS,
which the client receives as input to the protocol. This proof does not reveal the server's
private key to the client. This interaction is shown below.The POPRF mode extends the VOPRF mode such that the client and
server can additionally provide the public input info, which is used in computing
the PRF. That is, the client and server interact to compute
output = F(skS, input, info), as is shown below.Each protocol consists of an offline setup phase and an online phase,
as described in Sections and , respectively. Configuration details
for the offline phase are described in .ConfigurationEach of the three protocol variants are identified with a one-byte value (in hexadecimal):Identifiers for Protocol Variants
Mode
Value
modeOPRF
0x00
modeVOPRF
0x01
modePOPRF
0x02
Additionally, each protocol variant is instantiated with a ciphersuite
or suite. Each ciphersuite is identified with an ASCII string identifier,
referred to as identifier; see for the set of initial
ciphersuite values.The mode and ciphersuite identifier values are combined to create a
"context string" used throughout the protocol with the following function:Key Generation and Context SetupIn the offline setup phase, the server generates a fresh, random key
pair (skS, pkS). There are two ways to generate this key pair.
The first of which is using the GenerateKeyPair function described below.The second way to generate the key pair is via the deterministic key
generation function DeriveKeyPair, as described in .
Applications and implementations can use either method in practice.Also during the offline setup phase, both the client and server create a
context used for executing the online phase of the protocol after agreeing on a
mode and ciphersuite identifier. The context, such as OPRFServerContext,
is an implementation-specific data structure that stores a context string and
the relevant key material for each party.The OPRF variant server and client contexts are created as follows:The VOPRF variant server and client contexts are created as follows:The POPRF variant server and client contexts are created as follows:Deterministic Key GenerationThis section describes a deterministic key generation function, DeriveKeyPair.
It accepts a seed of 32 bytes generated from a cryptographically secure
random number generator and an optional (possibly empty) info string.
Note that, by design, knowledge of seed and info
is necessary to compute this function, which means that the secrecy of the
output private key (skS) depends on the secrecy of seed (since the info
string is public). 255:
raise DeriveKeyPairError
skS = G.HashToScalar(deriveInput || I2OSP(counter, 1),
DST = "DeriveKeyPair" || contextString)
counter = counter + 1
pkS = G.ScalarMultGen(skS)
return skS, pkS
]]>Online ProtocolIn the online phase, the client and server engage in a two-message protocol
to compute the protocol output. This section describes the protocol details
for each protocol variant. Throughout each description, the following parameters
are assumed to exist:
G:
a prime-order group implementing the API described in
contextString:
a PublicInput domain separation tag constructed during context setup, as created in
skS and pkS:
a Scalar and Element representing the private and public keys configured for the client and server in
Applications serialize protocol messages between the client and server for
transmission. Element values and Scalar values are serialized to byte arrays, and values
of type Proof are serialized as the concatenation of two serialized Scalar values.
Deserializing these values can fail; in which case, the application MUST abort
the protocol, raising a DeserializeError failure.Applications MUST check that input Element values received over the wire
are not the group identity element. This check is handled after deserializing
Element values; see for more information and requirements
on input validation for each ciphersuite.OPRF ProtocolThe OPRF protocol begins with the client blinding its input, as described
by the Blind function below. Note that this function can fail with an
InvalidInputError error for certain inputs that map to the group identity
element. Dealing with this failure is an application-specific decision;
see .Clients store blind locally and send blindedElement to the server for evaluation.
Upon receipt, servers process blindedElement using the BlindEvaluate function described
below.Servers send the output evaluatedElement to clients for processing.
Recall that servers may process multiple client inputs by applying the
BlindEvaluate function to each blindedElement received and returning an
array with the corresponding evaluatedElement values.Upon receipt of evaluatedElement, clients process it to complete the
OPRF evaluation with the Finalize function described below.An entity that knows both the private key and the input can compute the PRF
result using the following Evaluate function.VOPRF ProtocolThe VOPRF protocol begins with the client blinding its input, using the same
Blind function as in . Clients store the output blind locally
and send blindedElement to the server for evaluation. Upon receipt,
servers process blindedElement to compute an evaluated element and a DLEQ
proof using the following BlindEvaluate function.In the description above, inputs to GenerateProof are one-item
lists. Using larger lists allows servers to batch the evaluation of multiple
elements while producing a single batched DLEQ proof for them.The server sends both evaluatedElement and proof back to the client.
Upon receipt, the client processes both values to complete the VOPRF computation
using the Finalize function below.As in BlindEvaluate, inputs to VerifyProof are one-item lists. Clients can
verify multiple inputs at once whenever the server produced a batched DLEQ proof
for them.Finally, an entity that knows both the private key and the input can compute the PRF
result using the Evaluate function described in .POPRF ProtocolThe POPRF protocol begins with the client blinding its input, using the
following modified Blind function. In this step, the client also binds a
public info value, which produces an additional tweakedKey to be used later
in the protocol. Note that this function can fail with an
InvalidInputError error for certain private inputs that map to the group
identity element, as well as certain public inputs that, if not detected at
this point, will cause server evaluation to fail. Dealing with either failure
is an application-specific decision; see .Clients store the outputs blind and tweakedKey locally and send blindedElement to
the server for evaluation. Upon receipt, servers process blindedElement to
compute an evaluated element and a DLEQ proof using the following BlindEvaluate function.In the description above, inputs to GenerateProof are one-item
lists. Using larger lists allows servers to batch the evaluation of multiple
elements while producing a single batched DLEQ proof for them.BlindEvaluate triggers InverseError when the function is about to
calculate the inverse of a zero scalar, which does not exist and therefore
yields a failure in the protocol.
This only occurs for info values that map to the private key of the server. Thus,
clients that cause this error should be assumed to know the server private key. Hence,
this error can be a signal for the server to replace its private key.The server sends both evaluatedElement and proof back to the client.
Upon receipt, the client processes both values to complete the POPRF computation
using the Finalize function below.As in BlindEvaluate, inputs to VerifyProof are one-item lists.
Clients can verify multiple inputs at once whenever the server produced a
batched DLEQ proof for them.Finally, an entity that knows both the private key and the input can compute
the PRF result using the Evaluate function described below.CiphersuitesA ciphersuite (also referred to as 'suite' in this document) for the protocol
wraps the functionality required for the protocol to take place. The
ciphersuite should be available to both the client and server, and agreement
on the specific instantiation is assumed throughout.A ciphersuite contains instantiations of the following functionalities:
Group:
A prime-order group exposing the API detailed in , with the
generator element defined in the corresponding reference for each group. Each
group also specifies HashToGroup, HashToScalar, and serialization
functionalities. For
HashToGroup, the domain separation tag (DST) is constructed in accordance
with the recommendations in .
For HashToScalar, each group specifies an integer order that is used in
reducing integer values to a member of the corresponding scalar field.
Hash:
A cryptographic hash function whose output length is Nh bytes long.
This section includes an initial set of ciphersuites with supported groups
and hash functions. It also includes implementation details for each ciphersuite,
focusing on input validation. Future documents can specify additional ciphersuites
as needed, provided they meet the requirements in .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)This ciphersuite uses ristretto255 for the Group and SHA-512 for the hash
function. The value of the ciphersuite identifier is "ristretto255-SHA512".
Group:
ristretto255
Order():
Return 2^{252} + 27742317777372353535851937790883648493 (see ).
Identity():
As defined in .
Generator():
As defined in .
HashToGroup():
Use hash_to_ristretto255
with DST =
"HashToGroup-" || contextString and expand_message = expand_message_xmd
using SHA-512.
HashToScalar():
Compute uniform_bytes using expand_message = expand_message_xmd,
DST = "HashToScalar-" || contextString, and an output length of 64 bytes, interpret
uniform_bytes as a 512-bit integer in little-endian order, and reduce the
integer modulo Group.Order().
ScalarInverse(s):
Returns the multiplicative inverse of input Scalar s mod Group.Order().
RandomScalar():
Implemented by returning a uniformly random Scalar in the range
[0, G.Order() - 1]. Refer to for implementation guidance.
SerializeElement(A):
Implemented using the Encode function from ; Ne = 32.
DeserializeElement(buf):
Implemented using the Decode function from .
Additionally, this function validates that the resulting element is not the group
identity element. If these checks fail, deserialization returns an InputValidationError error.
SerializeScalar(s):
Implemented by outputting the little-endian, 32-byte encoding of
the Scalar value with the top three bits set to zero; Ns = 32.
DeserializeScalar(buf):
Implemented by attempting to deserialize a Scalar from a
little-endian, 32-byte string. This function can fail if the input does not
represent a Scalar in the range [0, G.Order() - 1]. Note that this means the
top three bits of the input MUST be zero.
Hash:
SHA-512; Nh = 64.
OPRF(decaf448, SHAKE-256)This ciphersuite uses decaf448 for the Group and SHAKE-256 for the hash
function. The value of the ciphersuite identifier is "decaf448-SHAKE256".
Implemented by returning a uniformly random Scalar in the range
[0, G.Order() - 1]. Refer to for implementation guidance.
HashToGroup():
Use hash_to_decaf448
with DST =
"HashToGroup-" || contextString and expand_message = expand_message_xof
using SHAKE-256.
HashToScalar():
Compute uniform_bytes using expand_message = expand_message_xof,
DST = "HashToScalar-" || contextString, and output length 64, interpret
uniform_bytes as a 512-bit integer in little-endian order, and reduce the
integer modulo Group.Order().
ScalarInverse(s):
Returns the multiplicative inverse of input Scalar s mod Group.Order().
SerializeElement(A):
Implemented using the Encode function from ; Ne = 56.
DeserializeElement(buf):
Implemented using the Decode function from .
Additionally, this function validates that the resulting element is not the group
identity element. If these checks fail, deserialization returns an InputValidationError error.
SerializeScalar(s):
Implemented by outputting the little-endian, 56-byte encoding of
the Scalar value; Ns = 56.
DeserializeScalar(buf):
Implemented by attempting to deserialize a Scalar from a
little-endian, 56-byte string. This function can fail if the input does not
represent a Scalar in the range [0, G.Order() - 1].
Hash:
SHAKE-256; Nh = 64.
OPRF(P-256, SHA-256)This ciphersuite uses P-256 for the Group and SHA-256 for the hash
function. The value of the ciphersuite identifier is "P256-SHA256".
Implemented by returning a uniformly random Scalar in the range
[0, G.Order() - 1]. Refer to for implementation guidance.
HashToGroup():
Use hash_to_curve with suite P256_XMD:SHA-256_SSWU_RO_
and DST =
"HashToGroup-" || contextString.
HashToScalar():
Use hash_to_field from
using L = 48, expand_message_xmd with SHA-256,
DST = "HashToScalar-" || contextString, and a
prime modulus equal to Group.Order().
ScalarInverse(s):
Returns the multiplicative inverse of input Scalar s mod Group.Order().
SerializeElement(A):
Implemented using the compressed Elliptic-Curve-Point-to-Octet-String
method according to ; Ne = 33.
DeserializeElement(buf):
Implemented by attempting to deserialize a 33-byte input string to
a public key using the compressed Octet-String-to-Elliptic-Curve-Point method according to
and then performing partial public-key validation, as defined in Section 5.6.2.3.4 of . This includes checking that the
coordinates of the resulting point are in the correct range, that the point is on
the curve, and that the point is not the group identity element.
If these checks fail, deserialization returns an InputValidationError error.
SerializeScalar(s):
Implemented using the Field-Element-to-Octet-String conversion
according to ; Ns = 32.
DeserializeScalar(buf):
Implemented by attempting to deserialize a Scalar from a 32-byte
string using Octet-String-to-Field-Element from . This function can fail if the
input does not represent a Scalar in the range [0, G.Order() - 1].
Hash:
SHA-256; Nh = 32.
OPRF(P-384, SHA-384)This ciphersuite uses P-384 for the Group and SHA-384 for the hash
function. The value of the ciphersuite identifier is "P384-SHA384".
Implemented by returning a uniformly random Scalar in the range
[0, G.Order() - 1]. Refer to for implementation guidance.
HashToGroup():
Use hash_to_curve with suite P384_XMD:SHA-384_SSWU_RO_
and DST =
"HashToGroup-" || contextString.
HashToScalar():
Use hash_to_field from
using L = 72, expand_message_xmd with SHA-384,
DST = "HashToScalar-" || contextString, and a
prime modulus equal to Group.Order().
ScalarInverse(s):
Returns the multiplicative inverse of input Scalar s mod Group.Order().
SerializeElement(A):
Implemented using the compressed Elliptic-Curve-Point-to-Octet-String
method according to ; Ne = 49.
DeserializeElement(buf):
Implemented by attempting to deserialize a 49-byte array to
a public key using the compressed Octet-String-to-Elliptic-Curve-Point method according to
and then performing partial public-key validation, as defined in Section 5.6.2.3.4 of . This includes checking that the
coordinates of the resulting point are in the correct range, that the point is on
the curve, and that the point is not the point at infinity. Additionally, this function
validates that the resulting element is not the group identity element.
If these checks fail, deserialization returns an InputValidationError error.
SerializeScalar(s):
Implemented using the Field-Element-to-Octet-String conversion
according to ; Ns = 48.
DeserializeScalar(buf):
Implemented by attempting to deserialize a Scalar from a 48-byte
string using Octet-String-to-Field-Element from . This function can fail if the
input does not represent a Scalar in the range [0, G.Order() - 1].
Hash:
SHA-384; Nh = 48.
OPRF(P-521, SHA-512)This ciphersuite uses P-521 for the Group and SHA-512 for the hash
function. The value of the ciphersuite identifier is "P521-SHA512".
Implemented by returning a uniformly random Scalar in the range
[0, G.Order() - 1]. Refer to for implementation guidance.
HashToGroup():
Use hash_to_curve with suite P521_XMD:SHA-512_SSWU_RO_
and DST =
"HashToGroup-" || contextString.
HashToScalar():
Use hash_to_field from
using L = 98, expand_message_xmd with SHA-512,
DST = "HashToScalar-" || contextString, and a
prime modulus equal to Group.Order().
ScalarInverse(s):
Returns the multiplicative inverse of input Scalar s mod Group.Order().
SerializeElement(A):
Implemented using the compressed Elliptic-Curve-Point-to-Octet-String
method according to ; Ne = 67.
DeserializeElement(buf):
Implemented by attempting to deserialize a 67-byte input string to
a public key using the compressed Octet-String-to-Elliptic-Curve-Point method according to
and then performing partial public-key validation, as defined in Section 5.6.2.3.4 of . This includes checking that the
coordinates of the resulting point are in the correct range, that the point is on
the curve, and that the point is not the point at infinity. Additionally, this function
validates that the resulting element is not the group identity element.
If these checks fail, deserialization returns an InputValidationError error.
SerializeScalar(s):
Implemented using the Field-Element-to-Octet-String conversion
according to ; Ns = 66.
DeserializeScalar(buf):
Implemented by attempting to deserialize a Scalar from a 66-byte
string using Octet-String-to-Field-Element from . This function can fail if the
input does not represent a Scalar in the range [0, G.Order() - 1].
Hash:
SHA-512; Nh = 64.
Future CiphersuitesA critical requirement of implementing the prime-order group using
elliptic curves is a method to instantiate the function
HashToGroup, which maps inputs to group elements. In the elliptic
curve setting, this deterministically maps inputs (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 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.The DeserializeElement and DeserializeScalar functions instantiated for a
particular prime-order group corresponding to a ciphersuite MUST adhere to
the description in . Future ciphersuites MUST describe how input
validation is done for DeserializeElement and DeserializeScalar.Additionally, future ciphersuites must take care when choosing the
security level of the group. See for additional details.Random Scalar GenerationTwo popular algorithms for generating a random integer uniformly distributed in
the range [0, G.Order() - 1] are described in the following subsections.Rejection SamplingGenerate a random byte array with Ns bytes and attempt to map to a Scalar
by calling DeserializeScalar in constant time. If it succeeds, return the
result. If it fails, try again with another random byte array until the
procedure succeeds. Failure to implement DeserializeScalar in constant time
can leak information about the underlying corresponding Scalar.As an optimization, if the group order is very close to a power of
2, it is acceptable to omit the rejection test completely. In
particular, if the group order is p and there is an integer b
such that |p - 2^{b}| is less than 2^{(b/2)}, then
RandomScalar can simply return a uniformly random integer of at
most b bits.Random Number Generation Using Extra Random BitsGenerate a random byte array with L = ceil(((3 * ceil(log2(G.Order()))) / 2) / 8)
bytes, and interpret it as an integer; reduce the integer modulo G.Order(), and return the
result. See for the underlying derivation of L.Application ConsiderationsThis section describes considerations for applications, including external interface
recommendations, explicit error treatment, and public input representation for the
POPRF protocol variant.Input LimitsApplication inputs, expressed as PrivateInput or PublicInput values, MUST be smaller
than 2^{16} - 1 bytes in length. Applications that require longer inputs can use a cryptographic
hash function to map these longer inputs to a fixed-length input that fits within the
PublicInput or PrivateInput length bounds. Note that some cryptographic hash functions
have input length restrictions themselves, but these limits are often large enough to
not be a concern in practice. For example, SHA-256 has an input limit of 2^{61} bytes.External Interface RecommendationsIn , the interface of the protocol functions allows that some inputs
(and outputs) to be group Element and Scalar values. However, implementations can
instead operate over Element and Scalar values internally and only expose
interfaces that operate with an application-specific format of messages.Error ConsiderationsSome OPRF variants specified in this document have fallible operations. For example, Finalize
and BlindEvaluate can fail if any element received from the peer fails input validation.
The explicit errors generated throughout this specification, along with the
conditions that lead to each error, are as follows:
VerifyError:
Verifiable OPRF proof verification failed (Sections and ).
DeserializeError:
Group Element or Scalar deserialization failure (Sections and ).
InputValidationError:
Validation of byte array inputs failed ().
There are other explicit errors generated in this specification; however, they occur with
negligible probability in practice. We note them here for completeness.
InvalidInputError:
OPRF Blind input produces an invalid output element (Sections and ).
InverseError:
A tweaked private key is invalid, i.e., has no multiplicative inverse (Sections and ).
In general, the errors in this document are meant as a guide to implementors.
They are not an exhaustive list of all the errors an implementation might emit.
For example, implementations might run out of memory and return a corresponding error.POPRF Public InputFunctionally, the VOPRF and POPRF variants differ in that the POPRF variant
admits public input, whereas the VOPRF variant does not. Public input allows
clients and servers to cryptographically bind additional data to the POPRF output.
A POPRF with fixed public input is functionally equivalent to a VOPRF. However, there
are differences in the underlying security assumptions made about each variant;
see for more details.This public input is known to both parties at the start of the protocol. It is RECOMMENDED
that this public input be constructed with some type of higher-level domain separation
to avoid cross protocol attacks or related issues. For example, protocols using
this construction might ensure that the public input uses a unique, prefix-free encoding.
See for further discussion on
constructing domain separation values.Implementations of the POPRF may choose to not let applications control info in
cases where this value is fixed or otherwise not useful to the application. In this
case, the resulting protocol is functionally equivalent to the VOPRF, which does not
admit public input.IANA ConsiderationsThis document has no IANA actions.Security ConsiderationsThis section discusses the security of the protocols defined in this specification, along
with some suggestions and trade-offs that arise from the implementation
of the protocol variants in this document. Note that the syntax of the POPRF
variant is different from that of the OPRF and VOPRF variants since it
admits an additional public input, but the same security considerations apply.Security PropertiesThe security properties of an OPRF protocol with functionality y = F(k, x)
include those of a standard PRF. Specifically:
Pseudorandomness:
For a random sampling of k, 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.
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 nonmalleable (i.e., we cannot compute a new evaluation of F
from an existing evaluation). A genuinely random function will be
nonmalleable with high probability, so a pseudorandom function must
be nonmalleable to maintain indistinguishability.
Unconditional input secrecy:
The server does not learn anything about
the client input x, even with unbounded computation.
In other words, an attacker with infinite computing power cannot recover any
information about the client's private input x from an invocation of the
protocol.Essentially, input secrecy is the property that, even if the server learns
the client's private input x at some point in the future, the server cannot
link any particular PRF evaluation to x. This property is
also known as unlinkability .Beyond client input secrecy, in the OPRF protocol, the server learns nothing about
the output y of the function, nor does the client learn anything about the
server's private key k.For the VOPRF and POPRF protocol variants, there is an additional
security property:
Verifiable:
The client must only complete execution of the protocol if
it can successfully assert that the output it computes is
correct. This is taken with respect to the private key held by the
server.
Any VOPRF or POPRF that satisfies the 'verifiable' security property is known
as 'verifiable'. 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.Finally, the POPRF variant also has the following security property:
Partial obliviousness:
The client and server must be able to perform the
PRF on the client's private and public input. Both the client and server know
the public input, but similar to the OPRF and VOPRF protocols, the server
learns nothing about the client's private input or the output of the function,
and the client learns nothing about the server's private key.
This property becomes useful when dealing with key management operations, such as
the rotation of the server's keys. Note that partial obliviousness only applies
to the POPRF variant because neither the OPRF nor VOPRF variants accept public
input to the protocol.Since the POPRF variant has a different syntax than the OPRF and VOPRF variants,
i.e., y = F(k, x, info), the pseudorandomness property is generalized:
Pseudorandomness:
For a random sampling of k, F is pseudorandom if the output
y = F(k, x, info) on any input pairs (x, info) is indistinguishable from uniformly
sampling any element in F's range.
Security AssumptionsBelow, we discuss the cryptographic security of each protocol variant
from , relative to the necessary cryptographic assumptions
that need to be made.OPRF and VOPRF AssumptionsThe OPRF and VOPRF protocol variants in this document are based on .
In particular, the VOPRF construction is similar to the construction
with the following distinguishing properties:
This document does not use session identifiers to differentiate different instances of the protocol.
This document supports batching so that multiple evaluations can happen at once whilst only constructing
one DLEQ proof object. This is enabled using an established batching technique .
The pseudorandomness and input secrecy (and verifiability) of the OPRF (and
VOPRF) protocols in are based on the One-More Gap Computational
Diffie-Hellman assumption that is computationally difficult to solve in the corresponding prime-order group.
In , these properties are proven for one instance (i.e., one key) of
the VOPRF protocol and without batching. There is currently no security
analysis available for the VOPRF protocol described in this document in
a setting with multiple server keys or batching.POPRF AssumptionsThe POPRF construction in this document is based on the construction known
as 3HashSDHI, given by . The construction is identical to
3HashSDHI, except that this design can optionally perform multiple POPRF
evaluations in one batch, whilst only constructing one DLEQ proof object.
This is enabled using an established batching technique .Pseudorandomness, input secrecy, verifiability, and partial obliviousness of the POPRF variant is
based on the assumption that the One-More Gap Strong Diffie-Hellman Inversion (SDHI)
assumption from is computationally difficult to solve in the corresponding
prime-order group. Tyagi et al. show that both the One-More Gap Computational Diffie-Hellman assumption
and the One-More Gap SDHI assumption reduce to the q-DL (Discrete Log) assumption
in the algebraic group model for some q number of BlindEvaluate queries.
(The One-More Gap Computational Diffie-Hellman assumption was the hardness assumption used to
evaluate the OPRF and VOPRF designs based on , which is a predecessor
to the POPRF variant in .)Static Diffie-Hellman Attack and Security LimitsA side effect of the OPRF protocol variants in this document is that they allow
instantiation of an oracle for constructing static Diffie-Hellman (DH) samples; see and .
These attacks are meant to recover (bits of) the server private key.
Best-known attacks reduce the security of the prime-order group instantiation by log_2(Q) / 2
bits, where Q is the number of BlindEvaluate calls made by the attacker.As a result of this class of attacks, choosing prime-order groups with a 128-bit security
level instantiates an OPRF with a reduced security level of 128 - (log_2(Q) / 2) bits of security.
Moreover, such attacks are only possible for those certain applications where the
adversary can query the OPRF directly. Applications can mitigate against this problem
in a variety of ways, e.g., by rate-limiting client queries to BlindEvaluate or by
rotating private keys. In applications where such an oracle is not made available,
this security loss does not apply.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. Applications that admit the aforementioned
oracle functionality and that cannot tolerate discrete logarithm security
of lower than 128 bits are RECOMMENDED to choose groups that target a
higher security level, such as decaf448 (used by ciphersuite decaf448-SHAKE256),
P-384 (used by ciphersuite P384-SHA384), or P-521 (used by ciphersuite P521-SHA512).Domain SeparationApplications SHOULD construct input to the protocol to provide domain
separation. Any system that has multiple OPRF applications should
distinguish client inputs to ensure the OPRF results are separate.
Guidance for constructing info can be found in .Timing LeaksTo ensure no information is leaked during protocol execution, all
operations that use secret data MUST run in constant time. This includes
all prime-order group operations and proof-specific operations that
operate on secret data, including GenerateProof and BlindEvaluate.ReferencesNormative ReferencesThe ristretto255 and decaf448 GroupsRecommendation for pair-wise key-establishment schemes using discrete logarithm cryptographyInformative ReferencesPrivacy Passcommit 085380aThe Static Diffie-Hellman ProblemCerticom ResearchCerticom ResearchWallet Databases with ObserversAdvances in Cryptology - CRYPTO' 92, pp. 89-105Security Analysis of the Strong Diffie-Hellman ProblemAdvances in Cryptology - EUROCRYPT 2006, pp. 1-11How To Prove Yourself: Practical Solutions to Identification and Signature ProblemsAdvances in Cryptology - CRYPTO' 86, pp. 186-194Highly-Efficient and Composable Password-Protected Secret Sharing (Or: How to Protect Your Bitcoin Wallet Online)2016 IEEE European Symposium on Security and Privacy (EuroS&P)Round-Optimal Password-Protected Secret Sharing and T-PAKE in the Password-Only ModelLecture Notes in Computer Science, pp. 233-253SPHINX: A Password Store that Perfectly Hides Passwords from Itself2017 IEEE 37th International Conference on Distributed Computing Systems (ICDCS)A Fast and Simple Partially Oblivious PRF, with ApplicationsPrivacy Pass: Bypassing Internet Challenges AnonymouslyRoyal Holloway, University of London (work completed during an internship at Cloudflare), London , UKUniversity of Waterloo, Waterloo , BelgiumCloudflare, San Francisco, California , USAProceedings on Privacy Enhancing Technologies, vol. 2018, no. 3, pp. 164-180SEC 1: Elliptic Curve CryptographyStandards for Efficient Cryptography Group (SECG)Digital Signature Standard (DSS)National Institute of Standards and Technology (NIST)Test VectorsThis section includes test vectors for the protocol variants specified
in this document. For each ciphersuite specified in ,
there is a set of test vectors for the protocol when running the OPRF,
VOPRF, and POPRF modes. Each test vector lists the batch size for
the evaluation. Each test vector value is encoded as a hexadecimal
byte string. The fields of each test vector are described below.
"Input":
The private client input, an opaque byte string.
"Info":
The public info, an opaque byte string. Only present for POPRF test
vectors.
"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 BlindEvaluate(),
a serialized Element of Ne bytes long.
"Proof":
The serialized Proof output from GenerateProof() composed of
two serialized Scalar values, each Ns bytes long. Only present for
VOPRF and POPRF test vectors.
"ProofRandomScalar":
The random Scalar r computed in GenerateProof(), a
serialized Scalar of Ns bytes long. Only present for VOPRF and POPRF
test vectors.
"Output":
The protocol output, an opaque byte string of Nh bytes long.
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, denoted Seed, and info string, denoted KeyInfo, which are
listed as well, using the DeriveKeyPair function from .ristretto255-SHA512OPRF ModeTest Vector 1, Batch Size 1Test Vector 2, Batch Size 1VOPRF ModeTest Vector 1, Batch Size 1Test Vector 2, Batch Size 1Test Vector 3, Batch Size 2POPRF ModeTest Vector 1, Batch Size 1Test Vector 2, Batch Size 1Test Vector 3, Batch Size 2decaf448-SHAKE256OPRF ModeTest Vector 1, Batch Size 1Test Vector 2, Batch Size 1VOPRF ModeTest Vector 1, Batch Size 1Test Vector 2, Batch Size 1Test Vector 3, Batch Size 2POPRF ModeTest Vector 1, Batch Size 1Test Vector 2, Batch Size 1Test Vector 3, Batch Size 2P256-SHA256OPRF ModeTest Vector 1, Batch Size 1Test Vector 2, Batch Size 1VOPRF ModeTest Vector 1, Batch Size 1Test Vector 2, Batch Size 1Test Vector 3, Batch Size 2POPRF ModeTest Vector 1, Batch Size 1Test Vector 2, Batch Size 1Test Vector 3, Batch Size 2P384-SHA384OPRF ModeTest Vector 1, Batch Size 1Test Vector 2, Batch Size 1VOPRF ModeTest Vector 1, Batch Size 1Test Vector 2, Batch Size 1Test Vector 3, Batch Size 2POPRF ModeTest Vector 1, Batch Size 1Test Vector 2, Batch Size 1Test Vector 3, Batch Size 2P521-SHA512OPRF ModeTest Vector 1, Batch Size 1Test Vector 2, Batch Size 1VOPRF ModeTest Vector 1, Batch Size 1Test Vector 2, Batch Size 1Test Vector 3, Batch Size 2POPRF ModeTest Vector 1, Batch Size 1Test Vector 2, Batch Size 1Test Vector 3, Batch Size 2AcknowledgementsThis document resulted from the work of the Privacy Pass team
. The authors would also like to acknowledge helpful
conversations with . provided
additional review and comments on key consistency. ,
, , , , ,
, , and also provided
helpful input and contributions to the document.