Skip to content

Commit

Permalink
Some corrections, tests, updates (#20)
Browse files Browse the repository at this point in the history
* some corrections, tests, updates, ready for V1

Signed-off-by: bytemare <[email protected]>

---------

Signed-off-by: bytemare <[email protected]>
  • Loading branch information
bytemare authored Dec 26, 2023
1 parent 07922ef commit 748f6c8
Show file tree
Hide file tree
Showing 15 changed files with 257 additions and 210 deletions.
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 @@ type State struct {
// 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 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 @@ func (c Ciphersuite) new(mode Mode) *oprf {
return &oprf{
hash: hashes[c].Get(),
contextString: contextString(mode, c),
id: c,
ciphersuite: c,
mode: mode,
group: groups[c],
}
Expand Down Expand Up @@ -130,30 +130,34 @@ func (c Ciphersuite) KeyGen() *KeyPair {
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")
}

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.
Expand Down Expand Up @@ -189,19 +193,19 @@ func (c Ciphersuite) Server(mode Mode, privateKey []byte) (*Server, error) {

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
44 changes: 1 addition & 43 deletions server.go
Original file line number Diff line number Diff line change
Expand Up @@ -128,48 +128,6 @@ func (s *Server) EvaluateBatchWithRandom(blindedElements [][]byte, random, info
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 @@ func (s *Server) PublicKey() []byte {

// Ciphersuite returns the cipher suite used in the server's instance.
func (s *Server) Ciphersuite() Ciphersuite {
return s.oprf.id
return s.oprf.ciphersuite
}
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

0 comments on commit 748f6c8

Please sign in to comment.