Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Some corrections, tests, updates #20

Merged
merged 3 commits into from
Dec 26, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions .github/renovate.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
{
"$schema": "https://docs.renovatebot.com/renovate-schema.json",
"extends": [
"config:base"
]
}
2 changes: 2 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,8 @@ jobs:
# Codecov
- name: Codecov
uses: codecov/codecov-action@bbeaa140357942e4e8d8e15f1cd2f4e612f64c59 # pin@master
env:
CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }}
with:
file: .github/coverage.out

Expand Down
11 changes: 4 additions & 7 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,18 +1,15 @@
# (V)OPRF : (Verifiable) Oblivious Pseudorandom Functions

[![VOPRF](https://github.com/bytemare/voprf/actions/workflows/ci.yml/badge.svg)](https://github.com/bytemare/voprf/actions/workflows/ci.yml)
[![VOPRF](https://github.com/bytemare/voprf/actions/workflows/ci.yml/badge.svg?branch=)](https://github.com/bytemare/voprf/actions/workflows/ci.yml)
[![Go Reference](https://pkg.go.dev/badge/github.com/bytemare/voprf.svg)](https://pkg.go.dev/github.com/bytemare/voprf)
[![codecov](https://codecov.io/gh/bytemare/voprf/branch/main/graph/badge.svg?token=5bQfB0OctA)](https://codecov.io/gh/bytemare/voprf)

Package voprf provides abstracted access to Oblivious Pseudorandom Functions (OPRF) over elliptic curves.

This implementation supports the OPRF, VOPRF, and POPRF protocols as specified in the latest [internet draft](https://tools.ietf.org/html/draft-irtf-cfrg-voprf).
Package voprf provides abstracted access to Oblivious Pseudorandom Functions (OPRF) over Elliptic Curves as specified in
[RFC9497](https://datatracker.ietf.org/doc/rfc9497) and fully supports the OPRF, VOPRF, and POPRF protocols.

## Versioning

[SemVer](http://semver.org/) is used for versioning. For the versions available, see the [tags on this repository](https://github.com/bytemare/voprf/tags).

Minor v0.x versions match the corresponding CFRG draft version, the master branch implements the latest changes of [the draft development](https://github.com/cfrg/draft-irtf-cfrg-voprf).
[SemVer](http://semver.org) is used for versioning. For the versions available, see the [tags on this repository](https://github.com/bytemare/voprf/tags).

## Contributing

Expand Down
1 change: 0 additions & 1 deletion SECURITY.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@

## Supported Versions

The VOPRF protocol is still in the process of specification. Therefore, this implementation evolves with the draft.
Only the latest version will be benefit from security fixes. Maintainers of projects using this implementation of VOPRF are invited to update their dependency.

## Reporting a Vulnerability
Expand Down
2 changes: 1 addition & 1 deletion client_state.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@
// Export extracts the client's internal values that can be imported in another client for session resumption.
func (c *Client) Export() *State {
s := &State{
Identifier: c.id,
Identifier: c.ciphersuite,
TweakedKey: nil,
ServerPublicKey: nil,
Input: nil,
Expand All @@ -46,7 +46,7 @@
}

if len(c.input) != len(c.blind) {
panic("different number of input and blind values")

Check warning on line 49 in client_state.go

View check run for this annotation

Codecov / codecov/patch

client_state.go#L49

Added line #L49 was not covered by tests
}

s.Input = make([][]byte, len(c.input))
Expand All @@ -66,35 +66,35 @@
// RecoverClient returns a Client recovered form the state, from which a session can be resumed.
func (s *State) RecoverClient() (*Client, error) {
if s.Mode != OPRF && s.Mode != VOPRF && s.Mode != POPRF {
return nil, errParamInvalidMode

Check warning on line 69 in client_state.go

View check run for this annotation

Codecov / codecov/patch

client_state.go#L69

Added line #L69 was not covered by tests
}

if !s.Identifier.Available() {
return nil, errParamInvalidID

Check warning on line 73 in client_state.go

View check run for this annotation

Codecov / codecov/patch

client_state.go#L73

Added line #L73 was not covered by tests
}

c := s.Identifier.client(s.Mode)

if err := importPrecheck(s); err != nil {
return nil, err

Check warning on line 79 in client_state.go

View check run for this annotation

Codecov / codecov/patch

client_state.go#L79

Added line #L79 was not covered by tests
}

c.oprf = s.Identifier.new(s.Mode)

if err := c.importTweakedKey(s); err != nil {
return nil, err

Check warning on line 85 in client_state.go

View check run for this annotation

Codecov / codecov/patch

client_state.go#L85

Added line #L85 was not covered by tests
}

if err := c.importPublicKey(s); err != nil {
return nil, err

Check warning on line 89 in client_state.go

View check run for this annotation

Codecov / codecov/patch

client_state.go#L89

Added line #L89 was not covered by tests
}

if err := c.importBlinds(s); err != nil {
return nil, err

Check warning on line 93 in client_state.go

View check run for this annotation

Codecov / codecov/patch

client_state.go#L93

Added line #L93 was not covered by tests
}

if err := c.importBlinded(s); err != nil {
return nil, err

Check warning on line 97 in client_state.go

View check run for this annotation

Codecov / codecov/patch

client_state.go#L97

Added line #L97 was not covered by tests
}

return c, nil
Expand All @@ -102,15 +102,15 @@

func importPrecheck(state *State) error {
if len(state.Input) != len(state.Blinded) {
return errStateDiffInput

Check warning on line 105 in client_state.go

View check run for this annotation

Codecov / codecov/patch

client_state.go#L105

Added line #L105 was not covered by tests
}

if len(state.Blinded) != 0 && len(state.Blinded) != len(state.Blind) {
return errStateDiffBlind

Check warning on line 109 in client_state.go

View check run for this annotation

Codecov / codecov/patch

client_state.go#L109

Added line #L109 was not covered by tests
}

if state.Mode == VOPRF && state.ServerPublicKey == nil {
return errStateNoPubKey

Check warning on line 113 in client_state.go

View check run for this annotation

Codecov / codecov/patch

client_state.go#L113

Added line #L113 was not covered by tests
}

return nil
Expand All @@ -120,7 +120,7 @@
if state.TweakedKey != nil {
t := c.group.NewElement()
if err := t.Decode(state.TweakedKey); err != nil {
return fmt.Errorf("tweaked key - %w", err)

Check warning on line 123 in client_state.go

View check run for this annotation

Codecov / codecov/patch

client_state.go#L123

Added line #L123 was not covered by tests
}

c.tweakedKey = t
Expand All @@ -133,7 +133,7 @@
if state.ServerPublicKey != nil {
pk := c.group.NewElement()
if err := pk.Decode(state.ServerPublicKey); err != nil {
return fmt.Errorf("server public key - %w", err)

Check warning on line 136 in client_state.go

View check run for this annotation

Codecov / codecov/patch

client_state.go#L136

Added line #L136 was not covered by tests
}

c.serverPublicKey = pk
Expand All @@ -147,7 +147,7 @@
for i := 0; i < len(state.Blind); i++ {
blind := c.group.NewScalar()
if err := blind.Decode(state.Blind[i]); err != nil {
return fmt.Errorf("blind %d - %w", i, err)

Check warning on line 150 in client_state.go

View check run for this annotation

Codecov / codecov/patch

client_state.go#L150

Added line #L150 was not covered by tests
}

c.blind[i] = blind
Expand All @@ -166,7 +166,7 @@

blinded := c.group.NewElement()
if err := blinded.Decode(state.Blinded[i]); err != nil {
return fmt.Errorf("invalid blinded element: %w", err)

Check warning on line 169 in client_state.go

View check run for this annotation

Codecov / codecov/patch

client_state.go#L169

Added line #L169 was not covered by tests
}

c.blindedElement[i] = blinded
Expand Down
4 changes: 1 addition & 3 deletions doc.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,5 @@
// Package voprf provides abstracted access to Oblivious Pseudorandom Functions (OPRF)
// and VOPRF Oblivious Pseudorandom Functions (VOPRF) using Elliptic Curves (EC(V)OPRF).
//
// This work in progress implements https://tools.ietf.org/html/draft-irtf-cfrg-voprf
//
// Integrations can use either base or verifiable mode with additive or multiplicative operations.
// This implements RFC9497.
package voprf
2 changes: 1 addition & 1 deletion evaluation.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ import (

// Evaluation holds the serialized evaluated elements and serialized proof.
type Evaluation struct {
// Elements represents the unique serialization of an Elements
// Elements represents the unique serialization of Elements
Elements [][]byte `json:"e"`

// Proofs
Expand Down
19 changes: 13 additions & 6 deletions examples_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ package voprf_test

import (
"encoding/hex"
"fmt"

"github.com/bytemare/voprf"
)
Expand Down Expand Up @@ -41,6 +42,7 @@ func exchangeWithServer(blinded []byte, verifiable bool) []byte {
return ev
}

// This shows you how to set up and run the base OPRF client.
func Example_client() {
input := []byte("input")

Expand All @@ -52,13 +54,15 @@ func Example_client() {

// The client blinds the initial input, and sends this to the server.
blinded := client.Blind(input, nil)
fmt.Printf("Send these %d bytes to the server.\n", len(blinded))

// Exchange with the server is not covered here. The following call is to mock an exchange with a server.
ev := exchangeWithServer(blinded, false)
// Exchange with the server is not covered in this example. Let's say the server sends the following serialized
// evaluation.
evaluation, _ := hex.DecodeString("00010020b4d261d982c6edd2fea53e8a39c1df6393f23cb9d1b4768891ec2f43b8d8e831")

// The client needs to decode the evaluation to finalize the process.
eval := new(voprf.Evaluation)
if err := eval.Deserialize(ev); err != nil {
if err = eval.Deserialize(evaluation); err != nil {
panic(err)
}

Expand All @@ -67,9 +71,10 @@ func Example_client() {
if output == nil || err != nil {
panic(err)
}
// Output:
// Output:Send these 32 bytes to the server.
}

// This shows you how to set up and run the Verifiable OPRF client.
func Example_verifiableClient() {
ciphersuite := voprf.Ristretto255Sha512
input := []byte("input")
Expand All @@ -85,11 +90,11 @@ func Example_verifiableClient() {
blinded := client.Blind(input, nil)

// Exchange with the server is not covered here. The following call is to mock an exchange with a server.
ev := exchangeWithServer(blinded, true)
evaluation := exchangeWithServer(blinded, true)

// The client needs to decode the evaluation to finalize the process.
eval := new(voprf.Evaluation)
if err := eval.Deserialize(ev); err != nil {
if err := eval.Deserialize(evaluation); err != nil {
panic(err)
}

Expand All @@ -102,6 +107,7 @@ func Example_verifiableClient() {
// Output:
}

// This shows you how to set up and run the base OPRF server.
func Example_server() {
// We suppose the client sends this blinded element.
blinded, _ := hex.DecodeString("7eaf3d7cbe43d54637274342ce53578b2aba836f297f4f07997a6e1dced1c058")
Expand All @@ -123,6 +129,7 @@ func Example_server() {
// Output:
}

// This shows you how to set up and run the Verifiable OPRF server.
func Example_verifiableServer() {
privateKey, _ := hex.DecodeString("8132542d5ed08594e7522b5eac6bee38bab5868996c25a3fd2a7739be1856b04")

Expand Down
30 changes: 17 additions & 13 deletions oprf.go
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,7 @@
return &oprf{
hash: hashes[c].Get(),
contextString: contextString(mode, c),
id: c,
ciphersuite: c,
mode: mode,
group: groups[c],
}
Expand All @@ -87,17 +87,17 @@
switch c {
case Ristretto255Sha512, P256Sha256, P384Sha384, P521Sha512, Secp256k1:
break
default:
return false

Check warning on line 91 in oprf.go

View check run for this annotation

Codecov / codecov/patch

oprf.go#L90-L91

Added lines #L90 - L91 were not covered by tests
}

// Check for unregistered groups and hashes
if _, ok := groups[c]; !ok {
return false

Check warning on line 96 in oprf.go

View check run for this annotation

Codecov / codecov/patch

oprf.go#L96

Added line #L96 was not covered by tests
}

if _, ok := hashes[c]; !ok {
return false

Check warning on line 100 in oprf.go

View check run for this annotation

Codecov / codecov/patch

oprf.go#L100

Added line #L100 was not covered by tests
}

return true
Expand All @@ -121,7 +121,7 @@
}
}

return "", errParamInvalidID

Check warning on line 124 in oprf.go

View check run for this annotation

Codecov / codecov/patch

oprf.go#L124

Added line #L124 was not covered by tests
}

// KeyGen returns a fresh KeyPair for the given cipher suite.
Expand All @@ -130,47 +130,51 @@
pk := c.Group().Base().Multiply(sk)

return &KeyPair{
ID: c,
PublicKey: pk.Encode(),
SecretKey: sk.Encode(),
Ciphersuite: c,
PublicKey: pk,
SecretKey: sk,
}
}

// DeriveKeyPair deterministically generates a private and public key pair from input seed.
func (c Ciphersuite) DeriveKeyPair(mode Mode, seed, info []byte) (*group.Scalar, *group.Element) {
func (c Ciphersuite) DeriveKeyPair(mode Mode, seed, info []byte) *KeyPair {
dst := concatenate([]byte(deriveKeyPairDST), contextString(mode, c))
deriveInput := concatenate(seed, lengthPrefixEncode(info))

var counter uint8
var s *group.Scalar
var sk *group.Scalar

for s == nil || s.IsZero() {
for sk == nil || sk.IsZero() {
if counter > 255 {
panic("impossible to generate non-zero scalar")

Check warning on line 149 in oprf.go

View check run for this annotation

Codecov / codecov/patch

oprf.go#L149

Added line #L149 was not covered by tests
}

s = c.Group().HashToScalar(concatenate(deriveInput, []byte{counter}), dst)
sk = c.Group().HashToScalar(concatenate(deriveInput, []byte{counter}), dst)
counter++
}

return s, c.Group().Base().Multiply(s)
return &KeyPair{
Ciphersuite: c,
PublicKey: c.Group().Base().Multiply(sk),
SecretKey: sk,
}
}

// Client returns a (P|V)OPRF client. For the OPRF mode, serverPublicKey should be nil, and non-nil otherwise.
func (c Ciphersuite) Client(mode Mode, serverPublicKey []byte) (*Client, error) {
if mode != OPRF && mode != VOPRF && mode != POPRF {
return nil, errParamInvalidMode

Check warning on line 166 in oprf.go

View check run for this annotation

Codecov / codecov/patch

oprf.go#L166

Added line #L166 was not covered by tests
}

client := c.client(mode)

if mode == VOPRF || mode == POPRF {
if serverPublicKey == nil {
return nil, errParamNoPubKey

Check warning on line 173 in oprf.go

View check run for this annotation

Codecov / codecov/patch

oprf.go#L173

Added line #L173 was not covered by tests
}

if err := client.setServerPublicKey(serverPublicKey); err != nil {
return nil, err

Check warning on line 177 in oprf.go

View check run for this annotation

Codecov / codecov/patch

oprf.go#L177

Added line #L177 was not covered by tests
}
}

Expand All @@ -181,7 +185,7 @@
// If privateKey is nil, a new private/public key pair is created.
func (c Ciphersuite) Server(mode Mode, privateKey []byte) (*Server, error) {
if mode != OPRF && mode != VOPRF && mode != POPRF {
return nil, errParamInvalidMode

Check warning on line 188 in oprf.go

View check run for this annotation

Codecov / codecov/patch

oprf.go#L188

Added line #L188 was not covered by tests
}

return c.server(mode, privateKey)
Expand All @@ -189,19 +193,19 @@

type oprf struct {
hash *hash.Hash
id Ciphersuite
ciphersuite Ciphersuite
contextString []byte
mode Mode
group group.Group
}

func contextString(mode Mode, id Ciphersuite) []byte {
ctx := make([]byte, 0, len(Version)+3+len(id.String()))
func contextString(mode Mode, ciphersuite Ciphersuite) []byte {
ctx := make([]byte, 0, len(Version)+3+len(ciphersuite.String()))
ctx = append(ctx, Version...)
ctx = append(ctx, "-"...)
ctx = append(ctx, byte(mode))
ctx = append(ctx, "-"...)
ctx = append(ctx, id.String()...)
ctx = append(ctx, ciphersuite.String()...)

return ctx
}
Expand Down Expand Up @@ -229,12 +233,12 @@

func (c *Client) setServerPublicKey(serverPublicKey []byte) error {
if serverPublicKey == nil { // OPRF
return nil

Check warning on line 236 in oprf.go

View check run for this annotation

Codecov / codecov/patch

oprf.go#L236

Added line #L236 was not covered by tests
}

pub := c.group.NewElement()
if err := pub.Decode(serverPublicKey); err != nil {
return fmt.Errorf("invalid public key: %w", err)

Check warning on line 241 in oprf.go

View check run for this annotation

Codecov / codecov/patch

oprf.go#L241

Added line #L241 was not covered by tests
}

c.serverPublicKey = pub
Expand All @@ -254,7 +258,7 @@
} else {
sk := s.group.NewScalar()
if err := sk.Decode(privateKey); err != nil {
return nil, fmt.Errorf("invalid private key: %w", err)

Check warning on line 261 in oprf.go

View check run for this annotation

Codecov / codecov/patch

oprf.go#L261

Added line #L261 was not covered by tests
}

s.privateKey = sk
Expand Down Expand Up @@ -299,7 +303,7 @@
groups[c] = g
hashes[c] = h
} else {
panic(fmt.Sprintf("OPRF dependencies not available - Group: %v, Hash: %v", g.Available(), h.Available()))

Check warning on line 306 in oprf.go

View check run for this annotation

Codecov / codecov/patch

oprf.go#L306

Added line #L306 was not covered by tests
}
}

Expand Down
44 changes: 1 addition & 43 deletions server.go
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@
scalar = t.Copy().Invert()

if scalar.IsZero() {
return nil, nil, errZeroScalar

Check warning on line 47 in server.go

View check run for this annotation

Codecov / codecov/patch

server.go#L47

Added line #L47 was not covered by tests
}
} else {
scalar = s.privateKey
Expand All @@ -58,7 +58,7 @@
r.Random()
} else {
if err := r.Decode(random); err != nil {
return fmt.Errorf("decoding input random scalar: %w", err)

Check warning on line 61 in server.go

View check run for this annotation

Codecov / codecov/patch

server.go#L61

Added line #L61 was not covered by tests
}
}

Expand All @@ -78,7 +78,7 @@

scalar, t, err := s.getPrivateKeys(info)
if err != nil {
return nil, err

Check warning on line 81 in server.go

View check run for this annotation

Codecov / codecov/patch

server.go#L81

Added line #L81 was not covered by tests
}

var r *group.Scalar
Expand All @@ -88,7 +88,7 @@

r = s.group.NewScalar()
if err := setRandom(r, random); err != nil {
return nil, err

Check warning on line 91 in server.go

View check run for this annotation

Codecov / codecov/patch

server.go#L91

Added line #L91 was not covered by tests
}
}

Expand Down Expand Up @@ -128,48 +128,6 @@
return s.innerEvaluateBatch(blindedElements, random, info)
}

// FullEvaluate reproduces the full PRF but without the blinding operations, using the client's input.
// This should output the same digest as the client's Finalize() function.
func (s *Server) FullEvaluate(input, info []byte) ([]byte, error) {
p := s.HashToGroup(input)

scalar, _, err := s.getPrivateKeys(info)
if err != nil {
return nil, err
}

t := p.Multiply(scalar)

if s.oprf.mode == OPRF || s.oprf.mode == VOPRF {
info = nil
}

return s.hashTranscript(input, info, t.Encode()), nil
}

// VerifyFinalize takes the client input (the un-blinded element) and the client's finalize() output,
// and returns whether it can match the client's output.
func (s *Server) VerifyFinalize(input, info, output []byte) bool {
digest, err := s.FullEvaluate(input, info)
if err != nil {
return false
}

return ctEqual(digest, output)
}

// VerifyFinalizeBatch takes the batch of client input (the un-blinded elements) and the client's finalize() outputs,
// and returns whether it can match the client's outputs.
func (s *Server) VerifyFinalizeBatch(input, output [][]byte, info []byte) bool {
res := true

for i, in := range input {
res = s.VerifyFinalize(in, info, output[i])
}

return res
}

// PrivateKey returns the server's serialized private key.
func (s *Server) PrivateKey() []byte {
return s.privateKey.Encode()
Expand All @@ -182,5 +140,5 @@

// Ciphersuite returns the cipher suite used in the server's instance.
func (s *Server) Ciphersuite() Ciphersuite {
return s.oprf.id
return s.oprf.ciphersuite

Check warning on line 143 in server.go

View check run for this annotation

Codecov / codecov/patch

server.go#L143

Added line #L143 was not covered by tests
}
40 changes: 29 additions & 11 deletions tests/helper_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import (
"testing"

group "github.com/bytemare/crypto"
"github.com/bytemare/hash"

"github.com/bytemare/voprf"
)
Expand All @@ -33,29 +34,46 @@ type configuration struct {
curve elliptic.Curve
ciphersuite voprf.Ciphersuite
name string
hash hash.Hashing
group group.Group
}

var configurationTable = []configuration{
{
name: "Ristretto255",
ciphersuite: voprf.Ristretto255Sha512,
group: group.Ristretto255Sha512,
hash: hash.SHA512,
curve: nil,
},
{
name: "P256Sha256",
ciphersuite: voprf.P256Sha256,
group: group.P256Sha256,
hash: hash.SHA256,
curve: elliptic.P256(),
},
{
name: "P384Sha512",
ciphersuite: voprf.P384Sha384,
group: group.P384Sha384,
hash: hash.SHA384,
curve: elliptic.P384(),
},
{
name: "P521Sha512",
ciphersuite: voprf.P521Sha512,
group: group.P521Sha512,
hash: hash.SHA512,
curve: elliptic.P521(),
},
{
name: "Secp256k1Sha256",
ciphersuite: voprf.Secp256k1,
group: group.Secp256k1,
hash: hash.SHA256,
curve: nil,
},
}

func testAll(t *testing.T, f func(*configuration)) {
Expand Down Expand Up @@ -102,16 +120,16 @@ func randomBytes(length int) []byte {
return r
}

func getBadNistElement(t *testing.T, id group.Group) []byte {
size := id.ElementLength()
func getBadNistElement(t *testing.T, g group.Group) []byte {
size := g.ElementLength()
element := randomBytes(size)
// detag compression
element[0] = 4

// test if invalid compression is detected
err := id.NewElement().Decode(element)
err := g.NewElement().Decode(element)
if err == nil {
t.Errorf("detagged compressed point did not yield an error for group %s", id)
t.Errorf("detagged compressed point did not yield an error for group %s", g)
}

return element
Expand Down Expand Up @@ -183,19 +201,19 @@ func lengthPrefixEncode(input []byte) []byte {
return append(i2osp2(len(input)), input...)
}

func contextString(mode voprf.Mode, id voprf.Ciphersuite) []byte {
ctx := make([]byte, 0, len(voprf.Version)+3+len(id.String()))
func contextString(mode voprf.Mode, g voprf.Ciphersuite) []byte {
ctx := make([]byte, 0, len(voprf.Version)+3+len(g.String()))
ctx = append(ctx, voprf.Version...)
ctx = append(ctx, "-"...)
ctx = append(ctx, byte(mode))
ctx = append(ctx, "-"...)
ctx = append(ctx, id.String()...)
ctx = append(ctx, g.String()...)

return ctx
}

func deriveKeyPair(seed, info []byte, mode voprf.Mode, id voprf.Ciphersuite) (*group.Scalar, *group.Element) {
dst := concatenate([]byte(deriveKeyPairDST), contextString(mode, id))
func deriveKeyPair(seed, info []byte, mode voprf.Mode, g voprf.Ciphersuite) (*group.Scalar, *group.Element) {
dst := concatenate([]byte(deriveKeyPairDST), contextString(mode, g))
deriveInput := concatenate(seed, lengthPrefixEncode(info))

var counter uint8
Expand All @@ -206,9 +224,9 @@ func deriveKeyPair(seed, info []byte, mode voprf.Mode, id voprf.Ciphersuite) (*g
panic("impossible to generate non-zero scalar")
}

s = id.Group().HashToScalar(concatenate(deriveInput, []byte{counter}), dst)
s = g.Group().HashToScalar(concatenate(deriveInput, []byte{counter}), dst)
counter++
}

return s, id.Group().Base().Multiply(s)
return s, g.Group().Base().Multiply(s)
}
Loading
Loading