Back to index

OPRFs (Oblivious Pseudorandom Functions) for the Busy Dev

2023-01-06

An oblivious pseudorandom function (OPRF) is a cryptographic construct that allows two parties (i.e., the ubiquotous Alice and Bob) to perform a keyed hash function (also known as a "pseudorandom function") in such a way that one party (the "server") does not learn the input of the other party (the "client").

So imagine this: back in the day, you would take photos with your camera and have it developed into pictures (actual paper) at the photo studio. Now, the person developing those photos is looking at your private photos and, possibly, taking copies of it if they're creepy enough.

The problem here is that you (the client) want a serviceman (the server) to do with your data (the undeveloped photos in your camera) but you prefer to keep your data private.

An OPRF works like that: it's a way to compute a function (read: a number) on personal client data using the server's data without the server knowing anything about your data. It sounds weird, but let's give an example.

Flow

So, here's a picture and a small Go snippet to go with it with explanation:

package main

import (
	"crypto/rand"
	"fmt"

	"github.com/cloudflare/circl/oprf"
)

func main() {
	suite := oprf.SuiteP256

  // Step 1: Generate client and server secrets
	serverSecret, _ := oprf.GenerateKey(suite, rand.Reader)
	server := oprf.NewServer(suite, serverSecret)
  clientSecret := [][]byte{[]byte("bunnyfoofoo")}
	client := oprf.NewClient(suite)

  // Step 2: Client blinds their secret and sends it over to the server
	finData, evalReq, _ := client.Blind(clientSecret)

  // Step 3: Server receives the blinded secret and evaluates it. This is where
  // the server actually does a calculation **on** the blinded user secret.
	evaluation, _ := server.Evaluate(evalReq)

  // Step 4: Server is done with the evaluation over the blinded client secret.
  // They send it to over to the client. The client "finalizes" (read:
  // unblinds) the evaluation, yielding a computed function over the client
  // secret without the server ever reading the client secret. They only have ever
  // worked with the "blinded" client secret
	outputs, _ := client.Finalize(finData, evaluation)
	// [241 135 97 112 209 249 90 225 247 33 2 234 113 84 96 199 124 75 188 34
	//  20 7 243 88 249 176 188 134 199 198 106 169]
	fmt.Println(outputs[0])
}

Real-life Applications

This is useful in situations where the server needs to perform a computation on the client's data, but the client wants to keep its data private.

Signing up and logging in to websites is a major use case here. See this article by renowned cryptographer Matthew Green talking about a specific family of cryptographic protocols called "Password-authenticated Key Exchanges" (or, PAKE). I've also made a project that uses a PAKE for password authentication that you can be integrated into mobile apps and websites.

Another use case is pseudonymization: basically "blinding" a database so that the data, if breached, won't be indicative.