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 }