github.com/cloudflare/circl@v1.5.0/oprf/oprf.go (about)

     1  // Package oprf provides Verifiable, Oblivious Pseudo-Random Functions.
     2  //
     3  // An Oblivious Pseudorandom Function (OPRFs) is a two-party protocol for
     4  // computing the output of a PRF. One party (the server) holds the PRF secret
     5  // key, and the other (the client) holds the PRF input.
     6  //
     7  // This package is compatible with the OPRF specification at RFC-9497 [1].
     8  //
     9  // # Protocol Overview
    10  //
    11  // This diagram shows the steps of the protocol that are common for all operation modes.
    12  //
    13  //	Client(info*)                               Server(sk, pk, info*)
    14  //	=================================================================
    15  //	finData, evalReq = Blind(input)
    16  //
    17  //	                            evalReq
    18  //	                          ---------->
    19  //
    20  //	                            evaluation = Evaluate(evalReq, info*)
    21  //
    22  //	                           evaluation
    23  //	                          <----------
    24  //
    25  //	output = Finalize(finData, evaluation, info*)
    26  //
    27  // # Operation Modes
    28  //
    29  // Each operation mode provides different properties to the PRF.
    30  //
    31  // Base Mode: Provides obliviousness to the PRF evaluation, i.e., it ensures
    32  // that the server does not learn anything about the client's input and output
    33  // during the Evaluation step.
    34  //
    35  // Verifiable Mode: Extends the Base mode allowing the client to verify that
    36  // Server used the private key that corresponds to the public key.
    37  //
    38  // Partial Oblivious Mode: Extends the Verifiable mode by including shared
    39  // public information to the PRF input.
    40  //
    41  // All three modes can perform batches of PRF evaluations, so passing an array
    42  // of inputs will produce an array of outputs.
    43  //
    44  // # References
    45  //
    46  // [1] RFC-9497: https://www.rfc-editor.org/info/rfc9497
    47  package oprf
    48  
    49  import (
    50  	"crypto"
    51  	"encoding/binary"
    52  	"errors"
    53  	"hash"
    54  	"io"
    55  	"math"
    56  
    57  	"github.com/cloudflare/circl/group"
    58  	"github.com/cloudflare/circl/zk/dleq"
    59  )
    60  
    61  const (
    62  	version          = "OPRFV1-"
    63  	finalizeDST      = "Finalize"
    64  	hashToGroupDST   = "HashToGroup-"
    65  	hashToScalarDST  = "HashToScalar-"
    66  	deriveKeyPairDST = "DeriveKeyPair"
    67  	infoLabel        = "Info"
    68  )
    69  
    70  type Mode = uint8
    71  
    72  const (
    73  	BaseMode             Mode = 0x00
    74  	VerifiableMode       Mode = 0x01
    75  	PartialObliviousMode Mode = 0x02
    76  )
    77  
    78  func isValidMode(m Mode) bool {
    79  	return m == BaseMode || m == VerifiableMode || m == PartialObliviousMode
    80  }
    81  
    82  type Suite interface {
    83  	Identifier() string
    84  	Group() group.Group
    85  	Hash() crypto.Hash
    86  	cannotBeImplementedExternally()
    87  }
    88  
    89  var (
    90  	// SuiteRistretto255 represents the OPRF with Ristretto255 and SHA-512
    91  	SuiteRistretto255 Suite = params{identifier: "ristretto255-SHA512", group: group.Ristretto255, hash: crypto.SHA512}
    92  	// SuiteP256 represents the OPRF with P-256 and SHA-256.
    93  	SuiteP256 Suite = params{identifier: "P256-SHA256", group: group.P256, hash: crypto.SHA256}
    94  	// SuiteP384 represents the OPRF with P-384 and SHA-384.
    95  	SuiteP384 Suite = params{identifier: "P384-SHA384", group: group.P384, hash: crypto.SHA384}
    96  	// SuiteP521 represents the OPRF with P-521 and SHA-512.
    97  	SuiteP521 Suite = params{identifier: "P521-SHA512", group: group.P521, hash: crypto.SHA512}
    98  )
    99  
   100  func GetSuite(identifier string) (Suite, error) {
   101  	for _, suite := range []Suite{SuiteRistretto255, SuiteP256, SuiteP384, SuiteP521} {
   102  		if suite.Identifier() == identifier {
   103  			return suite, nil
   104  		}
   105  	}
   106  	return nil, ErrInvalidSuite
   107  }
   108  
   109  func NewClient(s Suite) Client {
   110  	p := s.(params)
   111  	p.m = BaseMode
   112  
   113  	return Client{client{p}}
   114  }
   115  
   116  func NewVerifiableClient(s Suite, server *PublicKey) VerifiableClient {
   117  	p, ok := s.(params)
   118  	if !ok || server == nil {
   119  		panic(ErrNoKey)
   120  	}
   121  	p.m = VerifiableMode
   122  
   123  	return VerifiableClient{client{p}, server}
   124  }
   125  
   126  func NewPartialObliviousClient(s Suite, server *PublicKey) PartialObliviousClient {
   127  	p, ok := s.(params)
   128  	if !ok || server == nil {
   129  		panic(ErrNoKey)
   130  	}
   131  	p.m = PartialObliviousMode
   132  
   133  	return PartialObliviousClient{client{p}, server}
   134  }
   135  
   136  func NewServer(s Suite, key *PrivateKey) Server {
   137  	p, ok := s.(params)
   138  	if !ok || key == nil {
   139  		panic(ErrNoKey)
   140  	}
   141  	p.m = BaseMode
   142  
   143  	return Server{server{p, key}}
   144  }
   145  
   146  func NewVerifiableServer(s Suite, key *PrivateKey) VerifiableServer {
   147  	p, ok := s.(params)
   148  	if !ok || key == nil {
   149  		panic(ErrNoKey)
   150  	}
   151  	p.m = VerifiableMode
   152  
   153  	return VerifiableServer{server{p, key}}
   154  }
   155  
   156  func NewPartialObliviousServer(s Suite, key *PrivateKey) PartialObliviousServer {
   157  	p, ok := s.(params)
   158  	if !ok || key == nil {
   159  		panic(ErrNoKey)
   160  	}
   161  	p.m = PartialObliviousMode
   162  
   163  	return PartialObliviousServer{server{p, key}}
   164  }
   165  
   166  type params struct {
   167  	m          Mode
   168  	group      group.Group
   169  	hash       crypto.Hash
   170  	identifier string
   171  }
   172  
   173  func (p params) cannotBeImplementedExternally() {}
   174  
   175  func (p params) String() string     { return p.Identifier() }
   176  func (p params) Group() group.Group { return p.group }
   177  func (p params) Hash() crypto.Hash  { return p.hash }
   178  func (p params) Identifier() string { return p.identifier }
   179  
   180  func (p params) getDST(name string) []byte {
   181  	return append(append(append(append(
   182  		[]byte{},
   183  		[]byte(name)...),
   184  		[]byte(version)...),
   185  		[]byte{p.m, byte('-')}...),
   186  		[]byte(p.identifier)...)
   187  }
   188  
   189  func (p params) scalarFromInfo(info []byte) (group.Scalar, error) {
   190  	if len(info) > math.MaxUint16 {
   191  		return nil, ErrInvalidInfo
   192  	}
   193  	lenInfo := []byte{0, 0}
   194  	binary.BigEndian.PutUint16(lenInfo, uint16(len(info)))
   195  	framedInfo := append(append(append([]byte{},
   196  		[]byte(infoLabel)...),
   197  		lenInfo...),
   198  		info...)
   199  
   200  	return p.group.HashToScalar(framedInfo, p.getDST(hashToScalarDST)), nil
   201  }
   202  
   203  func (p params) finalizeHash(h hash.Hash, input, info, element []byte) []byte {
   204  	h.Reset()
   205  	lenBuf := []byte{0, 0}
   206  
   207  	binary.BigEndian.PutUint16(lenBuf, uint16(len(input)))
   208  	mustWrite(h, lenBuf)
   209  	mustWrite(h, input)
   210  
   211  	if p.m == PartialObliviousMode {
   212  		binary.BigEndian.PutUint16(lenBuf, uint16(len(info)))
   213  		mustWrite(h, lenBuf)
   214  		mustWrite(h, info)
   215  	}
   216  
   217  	binary.BigEndian.PutUint16(lenBuf, uint16(len(element)))
   218  	mustWrite(h, lenBuf)
   219  	mustWrite(h, element)
   220  
   221  	mustWrite(h, []byte(finalizeDST))
   222  
   223  	return h.Sum(nil)
   224  }
   225  
   226  func (p params) getDLEQParams() (out dleq.Params) {
   227  	out.G = p.group
   228  	out.H = p.hash
   229  	out.DST = p.getDST("")
   230  
   231  	return
   232  }
   233  
   234  func mustWrite(h io.Writer, bytes []byte) {
   235  	bytesLen, err := h.Write(bytes)
   236  	if err != nil {
   237  		panic(err)
   238  	}
   239  	if len(bytes) != bytesLen {
   240  		panic("oprf: failed to write")
   241  	}
   242  }
   243  
   244  var (
   245  	ErrInvalidSuite       = errors.New("oprf: invalid suite")
   246  	ErrInvalidMode        = errors.New("oprf: invalid mode")
   247  	ErrDeriveKeyPairError = errors.New("oprf: key pair derivation failed")
   248  	ErrInvalidInput       = errors.New("oprf: invalid input")
   249  	ErrInvalidSeed        = errors.New("oprf: invalid seed size")
   250  	ErrInvalidInfo        = errors.New("oprf: invalid info")
   251  	ErrInvalidProof       = errors.New("oprf: proof verification failed")
   252  	ErrInverseZero        = errors.New("oprf: inverting a zero value")
   253  	ErrNoKey              = errors.New("oprf: must provide a key")
   254  )
   255  
   256  type (
   257  	Blind     = group.Scalar
   258  	Blinded   = group.Element
   259  	Evaluated = group.Element
   260  )
   261  
   262  // FinalizeData encapsulates data needed for Finalize step.
   263  type FinalizeData struct {
   264  	inputs  [][]byte
   265  	blinds  []Blind
   266  	evalReq *EvaluationRequest
   267  }
   268  
   269  // CopyBlinds copies the serialized blinds to use when deterministically
   270  // invoking DeterministicBlind.
   271  func (f FinalizeData) CopyBlinds() []Blind {
   272  	out := make([]Blind, len(f.blinds))
   273  	for i, b := range f.blinds {
   274  		out[i] = b.Copy()
   275  	}
   276  	return out
   277  }
   278  
   279  // EvaluationRequest contains the blinded elements to be evaluated by the Server.
   280  type EvaluationRequest struct {
   281  	Elements []Blinded
   282  }
   283  
   284  // Evaluation contains a list of elements produced during server's evaluation, and
   285  // for verifiable modes it also includes a proof.
   286  type Evaluation struct {
   287  	Elements []Evaluated
   288  	Proof    *dleq.Proof
   289  }