Skip to content
Open
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
7 changes: 7 additions & 0 deletions oprf/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package oprf

import (
"crypto/rand"
"math"

"github.com/cloudflare/circl/group"
"github.com/cloudflare/circl/zk/dleq"
Expand Down Expand Up @@ -100,6 +101,12 @@ func (c client) validate(f *FinalizeData, e *Evaluation) (err error) {

wantGroup := *c.params.group.Params()
for i := range l {
// finalizeHash frames the input with I2OSP(len(input), 2), so an input
// of 2^16 bytes or more silently wraps the length prefix. Reject it, as
// scalarFromInfo already does for info.
if len(f.inputs[i]) > math.MaxUint16 {
return ErrInvalidInput
}
blind := f.blinds[i]
blinded := f.evalReq.Elements[i]
evaluated := e.Elements[i]
Expand Down
6 changes: 6 additions & 0 deletions oprf/keys.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package oprf
import (
"encoding/binary"
"io"
"math"

"github.com/cloudflare/circl/group"
)
Expand Down Expand Up @@ -112,6 +113,11 @@ func DeriveKey(s Suite, mode Mode, seed, info []byte) (*PrivateKey, error) {
if len(seed) != 32 {
return nil, ErrInvalidSeed
}
// deriveInput frames info with I2OSP(len(info), 2), so info of 2^16 bytes
// or more silently wraps the length prefix.
if len(info) > math.MaxUint16 {
return nil, ErrInvalidInfo
}
p.m = mode

lenInfo := []byte{0, 0}
Expand Down
46 changes: 46 additions & 0 deletions oprf/oprf_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import (
"encoding"
"encoding/binary"
"fmt"
"math"
"testing"

"github.com/cloudflare/circl/group"
Expand Down Expand Up @@ -277,6 +278,51 @@ func TestDeterministicBlindRejectsInvalidBlind(t *testing.T) {
}
}

func TestRejectsOversizedInput(t *testing.T) {
// RFC 9497 frames the input and info with I2OSP(len, 2), so a length of
// 2^16 or more wraps the prefix. Reject those, but keep accepting the
// maximum admissible length so valid callers are unaffected.
key, err := GenerateKey(SuiteP256, rand.Reader)
test.CheckNoErr(t, err, "failed private key generation")

oversized := make([]byte, math.MaxUint16+1)
maxSized := make([]byte, math.MaxUint16)

t.Run("finalize", func(t *testing.T) {
client := NewClient(SuiteP256)
server := NewServer(SuiteP256, key)
finalize := func(input []byte) error {
finData, evalReq, err := client.Blind([][]byte{input})
test.CheckNoErr(t, err, "blind failed")
evaluation, err := server.Evaluate(evalReq)
test.CheckNoErr(t, err, "evaluate failed")
_, err = client.Finalize(finData, evaluation)
return err
}
if err := finalize(oversized); err != ErrInvalidInput {
t.Fatalf("got %v, want %v", err, ErrInvalidInput)
}
test.CheckNoErr(t, finalize(maxSized), "max-length input must be accepted")
})

t.Run("fullEvaluate", func(t *testing.T) {
server := NewServer(SuiteP256, key)
if _, err := server.FullEvaluate(oversized); err != ErrInvalidInput {
t.Fatalf("got %v, want %v", err, ErrInvalidInput)
}
_, err := server.FullEvaluate(maxSized)
test.CheckNoErr(t, err, "max-length input must be accepted")
})

t.Run("deriveKey", func(t *testing.T) {
if _, err := DeriveKey(SuiteP256, BaseMode, make([]byte, 32), oversized); err != ErrInvalidInfo {
t.Fatalf("got %v, want %v", err, ErrInvalidInfo)
}
_, err := DeriveKey(SuiteP256, BaseMode, make([]byte, 32), maxSized)
test.CheckNoErr(t, err, "max-length info must be accepted")
})
}

func TestFinalizeRejectsMalformedState(t *testing.T) {
key, err := GenerateKey(SuiteP256, rand.Reader)
test.CheckNoErr(t, err, "failed private key generation")
Expand Down
7 changes: 7 additions & 0 deletions oprf/server.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package oprf
import (
"crypto/rand"
"crypto/subtle"
"math"

"github.com/cloudflare/circl/group"
"github.com/cloudflare/circl/zk/dleq"
Expand Down Expand Up @@ -91,6 +92,12 @@ func (s server) secretFromInfo(info []byte) (t, tInv group.Scalar, err error) {
}

func (s server) fullEvaluate(input, info []byte) ([]byte, error) {
// finalizeHash frames the input with I2OSP(len(input), 2), so an input of
// 2^16 bytes or more silently wraps the length prefix. Reject it, as
// scalarFromInfo already does for info.
if len(input) > math.MaxUint16 {
return nil, ErrInvalidInput
}
evalSecret := s.privateKey.k
if s.params.m == PartialObliviousMode {
var err error
Expand Down
Loading