-
Notifications
You must be signed in to change notification settings - Fork 4
/
Copy pathoprf.go
147 lines (116 loc) · 5.11 KB
/
oprf.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
// SPDX-License-Identifier: MIT
//
// Copyright (C) 2024 Daniel Bourdrez. All Rights Reserved.
//
// This source code is licensed under the MIT license found in the
// LICENSE file in the root directory of this source tree or at
// https://spdx.org/licenses/MIT.html
// Package voprf implements RFC9497 and provides abstracted access to Oblivious Pseudorandom Functions (OPRF) and
// Threshold Oblivious Pseudorandom Functions (TOPRF) using Elliptic Curve Prime Order Groups (EC-OPRF).
// For VOPRF and POPRF use the github.com/bytemare/oprf/voprf package.
package voprf
import (
"errors"
group "github.com/bytemare/crypto"
"github.com/bytemare/voprf/internal"
)
// Ciphersuite of the xOPRF compatible cipher suite to be used.
type Ciphersuite byte
const (
// Ristretto255Sha512 identifies the Ristretto255 group and SHA-512.
Ristretto255Sha512 = Ciphersuite(group.Ristretto255Sha512)
// decaf448Shake256 identifies the Decaf448 group and Shake-256. Not supported.
// decaf448Shake256 = 2.
// P256Sha256 identifies the NIST P-256 group and SHA-256.
P256Sha256 = Ciphersuite(group.P256Sha256)
// P384Sha384 identifies the NIST P-384 group and SHA-384.
P384Sha384 = Ciphersuite(group.P384Sha384)
// P521Sha512 identifies the NIST P-512 group and SHA-512.
P521Sha512 = Ciphersuite(group.P521Sha512)
// Secp256k1 identifies the SECp256k1 group and SHA-256.
Secp256k1 = Ciphersuite(group.Secp256k1)
)
var (
errBatchNoElements = errors.New("no evaluated elements provided to Finalize()")
errBatchDifferentSize = errors.New("number of evaluations is different thant number of previously blinded inputs")
)
// FromGroup returns a Ciphersuite given a Group.
func FromGroup(g group.Group) Ciphersuite {
return Ciphersuite(g)
}
// Group returns the elliptic curve prime-order group of the ciphersuite.
func (c Ciphersuite) Group() group.Group {
return group.Group(c)
}
// Name returns the [RFC9497](https://datatracker.ietf.org/doc/rfc9497) compliant identifier of the ciphersuite.
func (c Ciphersuite) Name() string {
return internal.CiphersuiteIdentifier[group.Group(c)]
}
// DeriveKeyPair returns a private-public key pair for the OPRF mode, given a secret seed and instance specific info.
// VOPRF and POPRF keys must be created with server.DeriveKeyPair() in the voprf package.
// TOPRF key pairs should be created using a distributed key generation protocol.
func DeriveKeyPair(c Ciphersuite, seed, info []byte) (*group.Scalar, *group.Element) {
// We don't use this as a method to a Ciphersuite, as it might be confusing when in VOPRF or POPRF mode, which
// use the Ciphersuite identifier from this package.
return internal.LoadConfiguration(c.Group(), internal.OPRF).DeriveKeyPair(seed, info)
}
// Client returns an OPRF client.
func (c Ciphersuite) Client() *Client {
return &Client{
Client: internal.NewClient(internal.OPRF, group.Group(c)),
}
}
// Client is used for OPRF and TOPRF client executions.
type Client struct {
*internal.Client
}
// SetBlind sets one or multiple blinds in the client's blind register. This is optional, and useful if you want to
// force usage of specific blinding scalar. If no blinding scalars are set, new, random blinds will be used.
func (c *Client) SetBlind(blind ...*group.Scalar) {
c.Client.UpdateStateCapacity(len(blind))
for i, b := range blind {
c.Client.SetBlind(i, b)
}
}
// Blind blinds the input using the first blinding scalar in the Client's register. If no blinding scalars were
// previously set, new, random blinds will be used.
func (c *Client) Blind(input []byte) *group.Element {
return c.Client.Blind(0, input)
}
// BlindBatch blinds the given set, using either previously set blinds in the same order (if they have been set) or
// newly generated random blinds. Note that if not enough blinds were set, new, random blinds will be used as necessary.
func (c *Client) BlindBatch(inputs [][]byte) []*group.Element {
c.UpdateStateCapacity(len(inputs))
blindedInput := make([]*group.Element, len(inputs))
for i, in := range inputs {
blindedInput[i] = c.Client.Blind(i, in)
}
return blindedInput
}
// Finalize unblinds the evaluated element and returns the protocol output.
func (c *Client) Finalize(evaluated *group.Element) []byte {
return c.Client.Finalize(0, evaluated)
}
// FinalizeBatch unblinds the evaluated elements and returns the corresponding protocol outputs.
func (c *Client) FinalizeBatch(evaluated []*group.Element) ([][]byte, error) {
if len(evaluated) == 0 {
return nil, errBatchNoElements
}
if len(evaluated) != c.Size() {
return nil, errBatchDifferentSize
}
return c.Client.FinalizeBatch(evaluated)
}
// Evaluate is the server's function to evaluate a Client provided blinded element with the server's secret key.
func Evaluate(key *group.Scalar, blinded *group.Element) *group.Element {
return blinded.Copy().Multiply(key)
}
// EvaluateBatch is the server's function to evaluate a set of Client provided blinded elements with the
// server's secret key.
func EvaluateBatch(key *group.Scalar, blinded []*group.Element) []*group.Element {
evaluated := make([]*group.Element, len(blinded))
for i, b := range blinded {
evaluated[i] = Evaluate(key, b)
}
return evaluated
}